summaryrefslogtreecommitdiffstats
path: root/src/detection/bluetoothradio/bluetoothradio_linux.c
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/bluetoothradio/bluetoothradio_linux.c
Add the files
Diffstat (limited to 'src/detection/bluetoothradio/bluetoothradio_linux.c')
-rw-r--r--src/detection/bluetoothradio/bluetoothradio_linux.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/src/detection/bluetoothradio/bluetoothradio_linux.c b/src/detection/bluetoothradio/bluetoothradio_linux.c
new file mode 100644
index 0000000..a7d92a4
--- /dev/null
+++ b/src/detection/bluetoothradio/bluetoothradio_linux.c
@@ -0,0 +1,153 @@
+#include "bluetoothradio.h"
+#include "common/strutil.h"
+
+#ifdef FF_HAVE_DBUS
+ #include "common/dbus.h"
+ #include "common/io.h"
+
+/* Example dbus reply:
+array [
+ dict entry(
+ string "Address"
+ variant string "XX:XX:XX:XX:XX:XX"
+ )
+ dict entry(
+ string "Name"
+ variant string "xxxxxxxx"
+ )
+ dict entry(
+ string "Powered"
+ variant boolean true
+ )
+ dict entry(
+ string "PowerState"
+ variant string "on"
+ )
+ dict entry(
+ string "Manufacturer"
+ variant uint16 2
+ )
+ dict entry(
+ string "Version"
+ variant byte 12
+ )
+]
+*/
+
+static const char* detectBluetoothProperty(FFBluetoothRadioResult* device, FFDBusData* dbus, DBusMessageIter* iter) {
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+ return "Expected dict entry";
+ }
+
+ DBusMessageIter dictIter;
+ dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
+
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) {
+ return "Expected dict entry key to be a string";
+ }
+
+ const char* deviceProperty;
+ dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &deviceProperty);
+
+ dbus->lib->ffdbus_message_iter_next(&dictIter);
+
+ if (ffStrEquals(deviceProperty, "Address")) {
+ ffDBusGetString(dbus, &dictIter, &device->address);
+ } else if (ffStrEquals(deviceProperty, "Alias")) {
+ ffDBusGetString(dbus, &dictIter, &device->name);
+ } else if (ffStrEquals(deviceProperty, "Manufacturer")) {
+ uint64_t vendorId;
+ if (ffDBusGetUint(dbus, &dictIter, &vendorId)) {
+ ffStrbufSetStatic(&device->vendor, ffBluetoothRadioGetVendor((uint32_t) vendorId));
+ }
+ } else if (ffStrEquals(deviceProperty, "Version")) {
+ uint64_t version;
+ if (ffDBusGetUint(dbus, &dictIter, &version)) {
+ device->lmpVersion = (int32_t) version;
+ }
+ } else if (ffStrEquals(deviceProperty, "Powered")) {
+ ffDBusGetBool(dbus, &dictIter, &device->enabled);
+ } else if (ffStrEquals(deviceProperty, "Discoverable")) {
+ ffDBusGetBool(dbus, &dictIter, &device->discoverable);
+ } else if (ffStrEquals(deviceProperty, "Pairable")) {
+ ffDBusGetBool(dbus, &dictIter, &device->connectable);
+ }
+
+ return NULL;
+}
+
+static const char* detectBluetoothRoot(FFBluetoothRadioResult* device, const char* hciName, FFDBusData* dbus) {
+ char objPath[300];
+ snprintf(objPath, sizeof(objPath), "/org/bluez/%s", hciName);
+
+ DBusMessage* properties = ffDBusGetMethodReply(dbus, "org.bluez", objPath, "org.freedesktop.DBus.Properties", "GetAll", "org.bluez.Adapter1", NULL);
+ if (!properties) {
+ return "Failed to call org.freedesktop.DBus.Properties.GetAll";
+ }
+
+ DBusMessageIter rootIter;
+ if (!dbus->lib->ffdbus_message_iter_init(properties, &rootIter)) {
+ dbus->lib->ffdbus_message_unref(properties);
+ return "Failed to get root iterator of org.freedesktop.DBus.Properties.GetAll";
+ }
+
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(&rootIter) != DBUS_TYPE_ARRAY) {
+ dbus->lib->ffdbus_message_unref(properties);
+ return "Expected array";
+ }
+
+ DBusMessageIter arrayIter;
+ dbus->lib->ffdbus_message_iter_recurse(&rootIter, &arrayIter);
+
+ do {
+ detectBluetoothProperty(device, dbus, &arrayIter);
+ } while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
+
+ dbus->lib->ffdbus_message_unref(properties);
+ return NULL;
+}
+
+static const char* detectBluetooth(FFlist* devices) {
+ FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/bluetooth");
+ if (dirp == NULL) {
+ return "Failed to open /sys/class/bluetooth";
+ }
+
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
+ const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus);
+ if (error) {
+ return error;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dirp)) != NULL) {
+ if (entry->d_name[0] == '.') {
+ continue;
+ }
+
+ if (strchr(entry->d_name, ':') != NULL) { // ignore connected devices
+ continue;
+ }
+
+ FFBluetoothRadioResult* device = FF_LIST_ADD(FFBluetoothRadioResult, *devices);
+ ffStrbufInit(&device->name);
+ ffStrbufInit(&device->address);
+ ffStrbufInitStatic(&device->vendor, "Unknown");
+ device->lmpVersion = INT_MIN;
+ device->lmpSubversion = INT_MIN;
+ device->enabled = false;
+ detectBluetoothRoot(device, entry->d_name, &dbus);
+ }
+ return NULL;
+}
+
+#endif
+
+const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */) {
+#ifdef FF_HAVE_DBUS
+ return detectBluetooth(devices);
+#else
+ FF_UNUSED(devices)
+ return "Fastfetch was compiled without DBus support";
+#endif
+}