1 use crate::btif::{BluetoothInterface, RawAddress, ToggleableProfile}; 2 use crate::topstack::get_dispatchers; 3 4 use std::sync::{Arc, Mutex}; 5 use topshim_macros::{cb_variant, profile_enabled_or}; 6 7 use log::warn; 8 9 #[cxx::bridge(namespace = bluetooth::topshim::rust)] 10 pub mod ffi { 11 unsafe extern "C++" { 12 include!("types/raw_address.h"); 13 #[namespace = ""] 14 type RawAddress = crate::btif::RawAddress; 15 } 16 17 #[derive(Debug, Copy, Clone)] 18 pub enum BtVcConnectionState { 19 Disconnected = 0, 20 Connecting, 21 Connected, 22 Disconnecting, 23 } 24 25 unsafe extern "C++" { 26 include!("vc/vc_shim.h"); 27 28 type VolumeControlIntf; 29 GetVolumeControlProfile(btif: *const u8) -> UniquePtr<VolumeControlIntf>30 unsafe fn GetVolumeControlProfile(btif: *const u8) -> UniquePtr<VolumeControlIntf>; 31 init(self: Pin<&mut VolumeControlIntf>)32 fn init(self: Pin<&mut VolumeControlIntf>); cleanup(self: Pin<&mut VolumeControlIntf>)33 fn cleanup(self: Pin<&mut VolumeControlIntf>); connect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)34 fn connect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); disconnect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)35 fn disconnect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); remove_device(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)36 fn remove_device(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); set_volume(self: Pin<&mut VolumeControlIntf>, group_id: i32, volume: u8)37 fn set_volume(self: Pin<&mut VolumeControlIntf>, group_id: i32, volume: u8); mute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)38 fn mute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); unmute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)39 fn unmute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); get_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, )40 fn get_ext_audio_out_volume_offset( 41 self: Pin<&mut VolumeControlIntf>, 42 addr: RawAddress, 43 ext_output_id: u8, 44 ); set_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, offset_val: i16, )45 fn set_ext_audio_out_volume_offset( 46 self: Pin<&mut VolumeControlIntf>, 47 addr: RawAddress, 48 ext_output_id: u8, 49 offset_val: i16, 50 ); get_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, )51 fn get_ext_audio_out_location( 52 self: Pin<&mut VolumeControlIntf>, 53 addr: RawAddress, 54 ext_output_id: u8, 55 ); set_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, location: u32, )56 fn set_ext_audio_out_location( 57 self: Pin<&mut VolumeControlIntf>, 58 addr: RawAddress, 59 ext_output_id: u8, 60 location: u32, 61 ); get_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, )62 fn get_ext_audio_out_description( 63 self: Pin<&mut VolumeControlIntf>, 64 addr: RawAddress, 65 ext_output_id: u8, 66 ); set_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, descr: *const c_char, )67 unsafe fn set_ext_audio_out_description( 68 self: Pin<&mut VolumeControlIntf>, 69 addr: RawAddress, 70 ext_output_id: u8, 71 descr: *const c_char, 72 ); 73 } 74 75 extern "Rust" { vc_connection_state_callback(state: BtVcConnectionState, addr: RawAddress)76 fn vc_connection_state_callback(state: BtVcConnectionState, addr: RawAddress); vc_volume_state_callback( address: RawAddress, volume: u8, mute: bool, is_autonomous: bool, )77 fn vc_volume_state_callback( 78 address: RawAddress, 79 volume: u8, 80 mute: bool, 81 is_autonomous: bool, 82 ); vc_group_volume_state_callback( group_id: i32, volume: u8, mute: bool, is_autonomous: bool, )83 fn vc_group_volume_state_callback( 84 group_id: i32, 85 volume: u8, 86 mute: bool, 87 is_autonomous: bool, 88 ); vc_device_available_callback(address: RawAddress, num_offset: u8)89 fn vc_device_available_callback(address: RawAddress, num_offset: u8); vc_ext_audio_out_volume_offset_callback( address: RawAddress, ext_output_id: u8, offset: i16, )90 fn vc_ext_audio_out_volume_offset_callback( 91 address: RawAddress, 92 ext_output_id: u8, 93 offset: i16, 94 ); vc_ext_audio_out_location_callback( address: RawAddress, ext_output_id: u8, location: u32, )95 fn vc_ext_audio_out_location_callback( 96 address: RawAddress, 97 ext_output_id: u8, 98 location: u32, 99 ); vc_ext_audio_out_description_callback( address: RawAddress, ext_output_id: u8, descr: String, )100 fn vc_ext_audio_out_description_callback( 101 address: RawAddress, 102 ext_output_id: u8, 103 descr: String, 104 ); 105 } 106 } 107 108 pub type BtVcConnectionState = ffi::BtVcConnectionState; 109 110 #[derive(Debug)] 111 pub enum VolumeControlCallbacks { 112 ConnectionState(BtVcConnectionState, RawAddress), 113 VolumeState(RawAddress, u8, bool, bool), 114 GroupVolumeState(i32, u8, bool, bool), 115 DeviceAvailable(RawAddress, u8), 116 ExtAudioOutVolume(RawAddress, u8, i16), 117 ExtAudioOutLocation(RawAddress, u8, u32), 118 ExtAudioOutDescription(RawAddress, u8, String), 119 } 120 121 pub struct VolumeControlCallbacksDispatcher { 122 pub dispatch: Box<dyn Fn(VolumeControlCallbacks) + Send>, 123 } 124 125 type VolumeControlCb = Arc<Mutex<VolumeControlCallbacksDispatcher>>; 126 127 cb_variant!(VolumeControlCb, 128 vc_connection_state_callback -> VolumeControlCallbacks::ConnectionState, 129 BtVcConnectionState, RawAddress); 130 131 cb_variant!(VolumeControlCb, 132 vc_volume_state_callback -> VolumeControlCallbacks::VolumeState, 133 RawAddress, u8, bool, bool); 134 135 cb_variant!(VolumeControlCb, 136 vc_group_volume_state_callback -> VolumeControlCallbacks::GroupVolumeState, 137 i32, u8, bool, bool); 138 139 cb_variant!(VolumeControlCb, 140 vc_device_available_callback -> VolumeControlCallbacks::DeviceAvailable, 141 RawAddress, u8); 142 143 cb_variant!(VolumeControlCb, 144 vc_ext_audio_out_volume_offset_callback -> VolumeControlCallbacks::ExtAudioOutVolume, 145 RawAddress, u8, i16); 146 147 cb_variant!(VolumeControlCb, 148 vc_ext_audio_out_location_callback -> VolumeControlCallbacks::ExtAudioOutLocation, 149 RawAddress, u8, u32); 150 151 cb_variant!(VolumeControlCb, 152 vc_ext_audio_out_description_callback -> VolumeControlCallbacks::ExtAudioOutDescription, 153 RawAddress, u8, String); 154 155 pub struct VolumeControl { 156 internal: cxx::UniquePtr<ffi::VolumeControlIntf>, 157 is_init: bool, 158 is_enabled: bool, 159 } 160 161 // For *const u8 opaque btif 162 // SAFETY: `VolumeControlIntf` is thread-safe to make calls from. 163 unsafe impl Send for VolumeControl {} 164 165 impl ToggleableProfile for VolumeControl { is_enabled(&self) -> bool166 fn is_enabled(&self) -> bool { 167 self.is_enabled 168 } 169 enable(&mut self) -> bool170 fn enable(&mut self) -> bool { 171 if self.is_enabled { 172 warn!("VolumeControl is already enabled."); 173 return false; 174 } 175 176 self.internal.pin_mut().init(); 177 self.is_enabled = true; 178 true 179 } 180 181 #[profile_enabled_or(false)] disable(&mut self) -> bool182 fn disable(&mut self) -> bool { 183 if !self.is_enabled { 184 warn!("VolumeControl is already disabled."); 185 return false; 186 } 187 188 self.internal.pin_mut().cleanup(); 189 self.is_enabled = false; 190 true 191 } 192 } 193 194 impl VolumeControl { new(intf: &BluetoothInterface) -> VolumeControl195 pub fn new(intf: &BluetoothInterface) -> VolumeControl { 196 let vc_if: cxx::UniquePtr<ffi::VolumeControlIntf>; 197 198 // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface` 199 vc_if = unsafe { ffi::GetVolumeControlProfile(intf.as_raw_ptr()) }; 200 201 VolumeControl { internal: vc_if, is_init: false, is_enabled: false } 202 } 203 is_initialized(&self) -> bool204 pub fn is_initialized(&self) -> bool { 205 self.is_init 206 } 207 208 // `internal.init` is invoked during `ToggleableProfile::enable` initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool209 pub fn initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool { 210 if self.is_init { 211 warn!("VolumeControl has already been initialized"); 212 return false; 213 } 214 215 if get_dispatchers().lock().unwrap().set::<VolumeControlCb>(Arc::new(Mutex::new(callbacks))) 216 { 217 panic!("Tried to set dispatcher for VolumeControl callbacks while it already exists"); 218 } 219 220 self.is_init = true; 221 222 true 223 } 224 225 #[profile_enabled_or] cleanup(&mut self)226 pub fn cleanup(&mut self) { 227 self.internal.pin_mut().cleanup(); 228 } 229 230 #[profile_enabled_or] connect(&mut self, addr: RawAddress)231 pub fn connect(&mut self, addr: RawAddress) { 232 self.internal.pin_mut().connect(addr); 233 } 234 235 #[profile_enabled_or] disconnect(&mut self, addr: RawAddress)236 pub fn disconnect(&mut self, addr: RawAddress) { 237 self.internal.pin_mut().disconnect(addr); 238 } 239 240 #[profile_enabled_or] remove_device(&mut self, addr: RawAddress)241 pub fn remove_device(&mut self, addr: RawAddress) { 242 self.internal.pin_mut().remove_device(addr); 243 } 244 245 #[profile_enabled_or] set_volume(&mut self, group_id: i32, volume: u8)246 pub fn set_volume(&mut self, group_id: i32, volume: u8) { 247 self.internal.pin_mut().set_volume(group_id, volume); 248 } 249 250 #[profile_enabled_or] mute(&mut self, addr: RawAddress)251 pub fn mute(&mut self, addr: RawAddress) { 252 self.internal.pin_mut().mute(addr); 253 } 254 255 #[profile_enabled_or] unmute(&mut self, addr: RawAddress)256 pub fn unmute(&mut self, addr: RawAddress) { 257 self.internal.pin_mut().unmute(addr); 258 } 259 260 #[profile_enabled_or] get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8)261 pub fn get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8) { 262 self.internal.pin_mut().get_ext_audio_out_volume_offset(addr, ext_output_id); 263 } 264 265 #[profile_enabled_or] set_ext_audio_out_volume_offset( &mut self, addr: RawAddress, ext_output_id: u8, offset_val: i16, )266 pub fn set_ext_audio_out_volume_offset( 267 &mut self, 268 addr: RawAddress, 269 ext_output_id: u8, 270 offset_val: i16, 271 ) { 272 self.internal.pin_mut().set_ext_audio_out_volume_offset(addr, ext_output_id, offset_val); 273 } 274 275 #[profile_enabled_or] get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8)276 pub fn get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8) { 277 self.internal.pin_mut().get_ext_audio_out_location(addr, ext_output_id); 278 } 279 280 #[profile_enabled_or] set_ext_audio_out_location( &mut self, addr: RawAddress, ext_output_id: u8, location: u32, )281 pub fn set_ext_audio_out_location( 282 &mut self, 283 addr: RawAddress, 284 ext_output_id: u8, 285 location: u32, 286 ) { 287 self.internal.pin_mut().set_ext_audio_out_location(addr, ext_output_id, location); 288 } 289 290 #[profile_enabled_or] get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8)291 pub fn get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8) { 292 self.internal.pin_mut().get_ext_audio_out_description(addr, ext_output_id); 293 } 294 295 #[profile_enabled_or] set_ext_audio_out_description( &mut self, addr: RawAddress, ext_output_id: u8, descr: String, )296 pub fn set_ext_audio_out_description( 297 &mut self, 298 addr: RawAddress, 299 ext_output_id: u8, 300 descr: String, 301 ) { 302 let c_descr = std::ffi::CString::new(descr).unwrap(); 303 unsafe { 304 // SAFETY: calling an FFI where the pointer is const, no modification. 305 self.internal.pin_mut().set_ext_audio_out_description( 306 addr, 307 ext_output_id, 308 c_descr.as_ptr(), 309 ); 310 } 311 } 312 } 313