diff options
Diffstat (limited to 'src/detection/bluetoothradio/bluetoothradio_linux.c')
| -rw-r--r-- | src/detection/bluetoothradio/bluetoothradio_linux.c | 153 |
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 +} |