laptou / bluez-rs

Control Bluetooth on Linux.
MIT License
39 stars 9 forks source link

Cannot set device class #18

Open schell opened 3 years ago

schell commented 3 years ago

Thank you for this nice crate. When trying to set up my rpi as a bluetooth keyboard I run into the following error, specifically while setting the device class:

Error: Xfer { source: Bluez { source: CommandError { opcode: SetDeviceClass, status: InvalidParams } } }

Where Xfer is my own Error enum.

This is roughly my program:

pub async fn run() -> Result<(), Error> {
    let mut client = BlueZClient::new().unwrap();

    let version = client.get_mgmt_version().await?;
    trace!(
        "management version: {}.{}",
        version.version,
        version.revision
    );

    let controllers = client.get_ext_controller_list().await?;
    let (controller, type_is, bus) = controllers.first().with_context(|| NoControllers)?;
    trace!(
        "got controller:\n{:?} ({:?}, {:?})",
        controller,
        type_is,
        bus
    );
    trace!(
        "info:\n{:#?}",
        client.get_controller_info(*controller).await?
    );

    let ctrl_settings = client
        .set_powered(
            *controller,
            true
        )
        .await?;
    trace!("controller settings after power on: {:?}", ctrl_settings);

    let ctrl_settings = client
        .set_connectable(
            *controller,
            true
        )
        .await?;
    trace!("controller settings after connectable: {:?}", ctrl_settings);

    let ctrl_settings = client
        .set_discoverable(
            *controller,
            DiscoverableMode::Limited,
            Some(60)
        )
        .await?;
    trace!("controller settings after discoverable: {:?}", ctrl_settings);

    let return_classes = client
        .set_device_class(
            *controller,
            DeviceClass::Peripheral {
                keyboard: true,
                pointer: false,
                class: PeripheralDeviceClass::Uncategorized,
            },
        )
        .await?;

    trace!("set device class, got: {:?}", return_classes);
    Ok(())
}

It's notable however, that using the command line to set the device class is working:

sudo hciconfig hci0 class 0x002540

After running the cli above, the settings have properly updated:

ControllerInfo {
    address: Address {
        bytes: [
            248,
            250,
            50,
            235,
            39,
            184,
        ],
    },
    bluetooth_version: 7,
    manufacturer: 15,
    supported_settings: BitFlags<ControllerSetting> {
        bits: 0b11011111111111111,
        flags: Powered | Connectable | FastConnectable | Discoverable | Pairable | LinkLevelSecurity | SecureSimplePairing | BREDR | HighSpeed | LE | Advertising | SecureConnection | DebugKeys | Privacy | StaticAddress | PhyConfiguration,
    },
    current_settings: BitFlags<ControllerSetting> {
        bits: 0b101011010011,
        flags: Powered | Connectable | Pairable | SecureSimplePairing | BREDR | LE | SecureConnection,
    },
    class_of_device: (
        Peripheral {
            keyboard: true,
            pointer: false,
            class: Uncategorized,
        },
        BitFlags<ServiceClass> {
            bits: 0b0,
        },
    ),
    name: "raspberrypi",
    short_name: "",
}

I'm new to working with bluetooth so I'm not sure what the problem is, but I thought it might be that the DeviceClass wasn't being converted into the correct u16 - indeed my device class seems to convert to 0x540, but making exec_command public and calling it with an explicit class of 0x2540 encounters the same error.

laptou commented 3 years ago

It is unclear why this is not working. The BlueZ documentation doesn't give any information about why 0x540 might be an invalid device class, and neither does the Bluetooth website.

schell commented 3 years ago

Yes, it seems weird. In the meantime I'm using the cli as a backup.

schell commented 3 years ago

I think it's notable that the 0x2000 part of 0x2540 sets the device in limited discovery mode. I thought maybe the problem was that setting 0x540 when the device is not already in limited discover would result in an invalid param error, so I set the device explicitly in limited discovery by first using set_discoverable. This doesn't seem to remedy the situation, though.