master ohmu/fastfetch-openbsd / src / detection / bluetoothradio / bluetoothradio_windows.c
ohmu  ·  2026-10-15
  1#include "bluetoothradio.h"
  2#include "common/debug.h"
  3#include "common/library.h"
  4#include "common/io.h"
  5#include "common/windows/unicode.h"
  6
  7#include <windows.h>
  8#include <bluetoothapis.h>
  9#include <winioctl.h>
 10
 11// #include <bthioctl.h>
 12
 13#define BTH_IOCTL_BASE 0
 14#define BTH_CTL(id) CTL_CODE(FILE_DEVICE_BLUETOOTH, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
 15#define IOCTL_BTH_GET_LOCAL_INFO BTH_CTL(BTH_IOCTL_BASE + 0x00)
 16#define LMP_LE_SUPPORTED(x) ((x >> 38) & 1)
 17
 18typedef struct [[gnu::packed]] _BTH_RADIO_INFO {
 19    // Supported LMP features of the radio.  Use LMP_XXX() to extract
 20    // the desired bits.
 21    ULONGLONG lmpSupportedFeatures;
 22
 23    // Manufacturer ID (possibly BTH_MFG_XXX)
 24    USHORT mfg;
 25
 26    // LMP subversion
 27    USHORT lmpSubversion;
 28
 29    // LMP version
 30    UCHAR lmpVersion;
 31} BTH_RADIO_INFO;
 32
 33typedef struct [[gnu::packed]] _BTH_LOCAL_RADIO_INFO {
 34    // Local BTH_ADDR, class of device, and radio name
 35    BTH_DEVICE_INFO localInfo;
 36
 37    // Combo of LOCAL_RADIO_XXX values
 38    ULONG flags;
 39
 40    // HCI revision, see core spec
 41    USHORT hciRevision;
 42
 43    // HCI version, see core spec
 44    UCHAR hciVersion;
 45
 46    // More information about the local radio (LMP, MFG)
 47    BTH_RADIO_INFO radioInfo;
 48} BTH_LOCAL_RADIO_INFO;
 49static_assert(sizeof(BTH_LOCAL_RADIO_INFO) == 292, "BTH_LOCAL_RADIO_INFO should be 292 bytes");
 50
 51#pragma GCC diagnostic ignored "-Wpointer-sign"
 52
 53const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */) {
 54    // Actually bluetoothapis.dll, but it's missing on Windows 7
 55    FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bluetoothapis.dll", 1)
 56    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstRadio)
 57    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextRadio)
 58    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindRadioClose)
 59    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothIsConnectable)
 60    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothIsDiscoverable)
 61
 62    HANDLE hRadio = nullptr;
 63    HBLUETOOTH_DEVICE_FIND hFind = ffBluetoothFindFirstRadio(&(BLUETOOTH_FIND_RADIO_PARAMS) {
 64                                                                 .dwSize = sizeof(BLUETOOTH_FIND_RADIO_PARAMS) },
 65        &hRadio);
 66    if (!hFind) {
 67        if (GetLastError() == ERROR_NO_MORE_ITEMS) {
 68            FF_DEBUG("No Bluetooth radios found or service disabled");
 69            return "No Bluetooth radios found or service disabled";
 70        } else {
 71            FF_DEBUG("BluetoothFindFirstRadio() failed: %s", ffDebugWin32Error(GetLastError()));
 72            return "BluetoothFindFirstRadio() failed";
 73        }
 74    }
 75
 76    do {
 77        FF_AUTO_CLOSE_FD HANDLE hCurrent = hRadio;
 78
 79        BTH_LOCAL_RADIO_INFO blri;
 80        DWORD returned;
 81        if (!DeviceIoControl(hCurrent, IOCTL_BTH_GET_LOCAL_INFO, nullptr, 0, &blri, sizeof(blri), &returned, nullptr)) {
 82            continue;
 83        }
 84
 85        FFBluetoothRadioResult* device = FF_LIST_ADD(FFBluetoothRadioResult, *devices);
 86        ffStrbufInitS(&device->name, blri.localInfo.name);
 87
 88        BLUETOOTH_ADDRESS_STRUCT addr = { .ullLong = blri.localInfo.address };
 89        ffStrbufInitF(&device->address, "%02X:%02X:%02X:%02X:%02X:%02X", addr.rgBytes[5], addr.rgBytes[4], addr.rgBytes[3], addr.rgBytes[2], addr.rgBytes[1], addr.rgBytes[0]);
 90
 91        device->lmpVersion = blri.radioInfo.lmpVersion;
 92        device->lmpSubversion = blri.radioInfo.lmpSubversion;
 93        ffStrbufInitStatic(&device->vendor, ffBluetoothRadioGetVendor(blri.radioInfo.mfg));
 94        device->enabled = true;
 95        device->connectable = ffBluetoothIsConnectable(hCurrent);
 96        device->discoverable = ffBluetoothIsDiscoverable(hCurrent);
 97    } while (ffBluetoothFindNextRadio(hFind, &hRadio));
 98
 99    ffBluetoothFindRadioClose(hFind);
100
101    return nullptr;
102}