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 // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface` 197 let vc_if: cxx::UniquePtr<ffi::VolumeControlIntf> = 198 unsafe { ffi::GetVolumeControlProfile(intf.as_raw_ptr()) }; 199 200 VolumeControl { internal: vc_if, is_init: false, is_enabled: false } 201 } 202 is_initialized(&self) -> bool203 pub fn is_initialized(&self) -> bool { 204 self.is_init 205 } 206 207 // `internal.init` is invoked during `ToggleableProfile::enable` initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool208 pub fn initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool { 209 if self.is_init { 210 warn!("VolumeControl has already been initialized"); 211 return false; 212 } 213 214 if get_dispatchers().lock().unwrap().set::<VolumeControlCb>(Arc::new(Mutex::new(callbacks))) 215 { 216 panic!("Tried to set dispatcher for VolumeControl callbacks while it already exists"); 217 } 218 219 self.is_init = true; 220 221 true 222 } 223 224 #[profile_enabled_or] cleanup(&mut self)225 pub fn cleanup(&mut self) { 226 self.internal.pin_mut().cleanup(); 227 } 228 229 #[profile_enabled_or] connect(&mut self, addr: RawAddress)230 pub fn connect(&mut self, addr: RawAddress) { 231 self.internal.pin_mut().connect(addr); 232 } 233 234 #[profile_enabled_or] disconnect(&mut self, addr: RawAddress)235 pub fn disconnect(&mut self, addr: RawAddress) { 236 self.internal.pin_mut().disconnect(addr); 237 } 238 239 #[profile_enabled_or] remove_device(&mut self, addr: RawAddress)240 pub fn remove_device(&mut self, addr: RawAddress) { 241 self.internal.pin_mut().remove_device(addr); 242 } 243 244 #[profile_enabled_or] set_volume(&mut self, group_id: i32, volume: u8)245 pub fn set_volume(&mut self, group_id: i32, volume: u8) { 246 self.internal.pin_mut().set_volume(group_id, volume); 247 } 248 249 #[profile_enabled_or] mute(&mut self, addr: RawAddress)250 pub fn mute(&mut self, addr: RawAddress) { 251 self.internal.pin_mut().mute(addr); 252 } 253 254 #[profile_enabled_or] unmute(&mut self, addr: RawAddress)255 pub fn unmute(&mut self, addr: RawAddress) { 256 self.internal.pin_mut().unmute(addr); 257 } 258 259 #[profile_enabled_or] get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8)260 pub fn get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8) { 261 self.internal.pin_mut().get_ext_audio_out_volume_offset(addr, ext_output_id); 262 } 263 264 #[profile_enabled_or] set_ext_audio_out_volume_offset( &mut self, addr: RawAddress, ext_output_id: u8, offset_val: i16, )265 pub fn set_ext_audio_out_volume_offset( 266 &mut self, 267 addr: RawAddress, 268 ext_output_id: u8, 269 offset_val: i16, 270 ) { 271 self.internal.pin_mut().set_ext_audio_out_volume_offset(addr, ext_output_id, offset_val); 272 } 273 274 #[profile_enabled_or] get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8)275 pub fn get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8) { 276 self.internal.pin_mut().get_ext_audio_out_location(addr, ext_output_id); 277 } 278 279 #[profile_enabled_or] set_ext_audio_out_location( &mut self, addr: RawAddress, ext_output_id: u8, location: u32, )280 pub fn set_ext_audio_out_location( 281 &mut self, 282 addr: RawAddress, 283 ext_output_id: u8, 284 location: u32, 285 ) { 286 self.internal.pin_mut().set_ext_audio_out_location(addr, ext_output_id, location); 287 } 288 289 #[profile_enabled_or] get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8)290 pub fn get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8) { 291 self.internal.pin_mut().get_ext_audio_out_description(addr, ext_output_id); 292 } 293 294 #[profile_enabled_or] set_ext_audio_out_description( &mut self, addr: RawAddress, ext_output_id: u8, descr: String, )295 pub fn set_ext_audio_out_description( 296 &mut self, 297 addr: RawAddress, 298 ext_output_id: u8, 299 descr: String, 300 ) { 301 let c_descr = std::ffi::CString::new(descr).unwrap(); 302 unsafe { 303 // SAFETY: calling an FFI where the pointer is const, no modification. 304 self.internal.pin_mut().set_ext_audio_out_description( 305 addr, 306 ext_output_id, 307 c_descr.as_ptr(), 308 ); 309 } 310 } 311 } 312