@@ -5,7 +5,9 @@
*
* Contact: <franz.haider@jolla.com>
*
- * Copyright (C) 2018 Jolla Ltd.
+ * Copyright (C) 2018-2022 Jolla Ltd.
+ *
+ * Based on bluez5/tools/btproxy.c:
* Copyright (C) 2011-2012 Intel Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
*
@@ -38,6 +40,10 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <linux/rfkill.h>
+#include <sys/ioctl.h>
#if USE_SYSTEMD
#include <systemd/sd-daemon.h>
@@ -62,6 +68,49 @@
#define BINDER_BLUETOOTH_SERVICE_IFACE_CALLBACKS "android.hardware.bluetooth@1.0::IBluetoothHciCallbacks"
#define BINDER_BLUETOOTH_SERVICE_SLOT "default"
+// After bluetooth has been enabled we need to process pending packets (vhci -> HAL)
+// immediately before anything else for example turning bluetooth on/off can interfere.
+#define PRIORITY_PROCESS_PACKETS_ONCE (G_PRIORITY_DEFAULT - 1)
+// Priority for host write packet must be higher than the gio channel (PRIORITY_HOST_READ_PACKETS)
+// otherwise we might process a second command
+// before the first one was accepted.
+// but it must be lower than the binder reply callback since otherwise we might send more packets
+// than the remote can handle.
+// it is defined in libgbinder as G_PRIORITY_DEFAULT and must stay in sync with this
+#define PRIORITY_HOST_WRITE_PACKET (G_PRIORITY_DEFAULT + 1)
+// The following two are the same since they are interleaved in the setup procedure.
+#define PRIORITY_HOST_READ_PACKETS (G_PRIORITY_DEFAULT + 2) // HAL -> vhci
+#define PRIORITY_CHECK_BT_STATE (G_PRIORITY_DEFAULT + 2)
+// This one is to be executed after the setup procedure to potentially turn bluetooth off
+// after the setup is complete if it was off before reboot for example.
+// Needs to be higher than the rfkill channel otherwise an event from the user
+// to turn bluetooth on/off might sneak in before we are done.
+#define PRIORITY_CHECK_BT_STATE_DONE (G_PRIORITY_DEFAULT + 3)
+
+// Priority of turning bluetooth on and off, should wait until binder callbacks
+// are completely handled.
+#define PRIORITY_RFKILL_CHANNEL (G_PRIORITY_DEFAULT + 4)
+
+enum bluetooth_codes {
+ INITIALIZE = GBINDER_FIRST_CALL_TRANSACTION,
+ SEND_HCI_COMMAND,
+ SEND_ACL_DATA,
+ SEND_SCO_DATA,
+ CLOSE,
+};
+
+enum bluetooth_callback_codes {
+ INITIALIZATION_COMPLETE = GBINDER_FIRST_CALL_TRANSACTION,
+ HCI_EVENT_RECEIVED,
+ ACL_DATA_RECEIVED,
+ SCO_DATA_RECEIVED,
+};
+
+struct pending_packet {
+ uint8_t *packet;
+ unsigned int size;
+};
+
struct proxy {
/* Receive commands, ACL and SCO data */
int host_fd;
@@ -74,9 +123,36 @@
int gio_channel_event_id;
int binder_replies_pending;
- gboolean init_failed;
+
+ int rfkill_fd;
+ GIOChannel *rfkill_channel;
+ int rfkill_watch_id;
+ int own_hci_index;
+
+ GBinderLocalObject *local_callbacks_object;
+ GBinderRemoteObject *remote;
+ GBinderServiceManager *sm;
+
+ bool bluetooth_on;
+ bool turn_off_bt_after_setup;
+ bool bluetooth_hal_initialized;
+
+ int death_id;
+
+ GList *pending_packets;
};
+static
+gboolean
+process_packets(
+ struct proxy *proxy);
+
+static
+void
+binder_remote_died(
+ GBinderRemoteObject* obj,
+ void* user_data);
+
void
handle_binder_reply(
GBinderClient* client,
@@ -125,19 +201,14 @@
gbinder_writer_append_hidl_vec(&writer, (void*)((char*)buf + 1), len - 1, sizeof(uint8_t));
proxy->binder_replies_pending++;
- // priority must be higher than the gio channel, otherwise we might process a second command
- // before the first one was accepted.
- // but it must be lower than the binder reply callback since otherwise we might send more packets
- // than the remote can handle.
- // it is defined in libgbinder as G_PRIORITY_DEFAULT_IDLE and must stay in sync with this
- g_idle_add_full(G_PRIORITY_DEFAULT_IDLE + 1, waiting_for_binder_reply, proxy, NULL);
+ g_idle_add_full(PRIORITY_HOST_WRITE_PACKET, waiting_for_binder_reply, proxy, NULL);
if (((uint8_t*)buf)[0] == HCI_COMMAND_PKT) {
- gbinder_client_transact(proxy->binder_client, 2 /* sendHciCommand */, 0, local_request, handle_binder_reply, NULL, proxy);
+ gbinder_client_transact(proxy->binder_client, SEND_HCI_COMMAND, 0, local_request, handle_binder_reply, NULL, proxy);
} else if (((uint8_t*)buf)[0] == HCI_ACLDATA_PKT) {
- gbinder_client_transact(proxy->binder_client, 3 /* sendAclData */, 0, local_request, handle_binder_reply, NULL, proxy);
+ gbinder_client_transact(proxy->binder_client, SEND_ACL_DATA, 0, local_request, handle_binder_reply, NULL, proxy);
} else if (((uint8_t*)buf)[0] == HCI_SCODATA_PKT) {
- gbinder_client_transact(proxy->binder_client, 4 /* sendScoData */, 0, local_request, handle_binder_reply, NULL, proxy);
+ gbinder_client_transact(proxy->binder_client, SEND_SCO_DATA, 0, local_request, handle_binder_reply, NULL, proxy);
} else {
fprintf(stderr, "Received incorrect packet type from HCI client.\n");
g_main_loop_quit(proxy->loop);
@@ -165,7 +236,7 @@
&error);
if (status == G_IO_STATUS_ERROR) {
- fprintf(stderr, "Writing packet to device failed: %s\n", error->message);
+ fprintf(stderr, "Writing packet from HAL to vhci device failed: %s\n", error->message);
// do not quit here, since this might happen if the user switches off
// bt but the hw still wants to send a final event.
return;
@@ -177,6 +248,60 @@
}
static
+void
+configure_bt(
+ struct proxy *proxy,
+ gboolean bluetooth_on)
+{
+ int status = 0;
+ bool fail = FALSE;
+
+ if (bluetooth_on && !proxy->bluetooth_on) {
+ GBinderRemoteReply *reply;
+ GBinderLocalRequest *initialize_request;
+
+ fprintf(stderr, "Turning bluetooth on\n");
+
+ initialize_request = gbinder_client_new_request(proxy->binder_client);
+
+ gbinder_local_request_append_local_object
+ (initialize_request, proxy->local_callbacks_object);
+
+ reply = gbinder_client_transact_sync_reply
+ (proxy->binder_client, INITIALIZE, initialize_request, &status);
+
+ if (status != GBINDER_STATUS_OK || !reply) {
+ fprintf(stderr, "ERROR: init reply: %p, %d\n", reply, status);
+ fail = TRUE;
+ }
+
+ gbinder_remote_reply_unref(reply);
+ gbinder_local_request_unref(initialize_request);
+ } else if (!bluetooth_on && proxy->bluetooth_on) {
+ GBinderRemoteReply *reply;
+
+ fprintf(stderr, "Turning bluetooth off\n");
+ proxy->bluetooth_hal_initialized = FALSE;
+
+ reply = gbinder_client_transact_sync_reply
+ (proxy->binder_client, CLOSE, NULL, &status);
+
+ if (status != GBINDER_STATUS_OK || !reply) {
+ fprintf(stderr, "ERROR: close reply: %p, %d\n", reply, status);
+ fail = TRUE;
+ }
+ gbinder_remote_reply_unref(reply);
+ } else {
+ fprintf(stderr, "WARNING: inconsistent bluetooth state: %d %d\n", proxy->bluetooth_on, bluetooth_on);
+ }
+ proxy->bluetooth_on = bluetooth_on;
+
+ if (fail) {
+ binder_remote_died(NULL, proxy);
+ }
+}
+
|