starnix_modules_android_usb/
lib.rs1#![recursion_limit = "256"]
6
7use fidl_fuchsia_hardware_usb_policy::DeviceState;
8use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
9use starnix_core::task::{CurrentTask, Kernel};
10use starnix_logging::log_error;
11use starnix_sync::{Locked, Unlocked};
12use starnix_uapi::errors::Errno;
13
14use fidl_fuchsia_usb_policy::PolicyProviderMarker;
15use fuchsia_component::client::connect_to_protocol;
16use starnix_core::device::kobject::{Device, UEventAction};
17use starnix_core::device::mem::DevNull;
18use starnix_core::device::simple_device_ops;
19use starnix_core::fs::sysfs::build_device_directory;
20use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
21use starnix_core::vfs::{FsStr, FsString};
22use starnix_uapi::file_mode::mode;
23use std::borrow::Cow;
24
25use std::future::Future;
26use std::sync::Arc;
27use std::sync::atomic::{AtomicU8, Ordering};
28
29#[repr(u8)]
30#[derive(Copy, Clone, Debug, PartialEq, Eq)]
31pub enum UsbGadgetState {
32 Disconnected = 0,
33 Configured = 1,
34 Connected = 2,
35 Unknown = 3,
36}
37
38impl From<u8> for UsbGadgetState {
39 fn from(val: u8) -> Self {
40 match val {
41 0 => UsbGadgetState::Disconnected,
42 1 => UsbGadgetState::Configured,
43 2 => UsbGadgetState::Connected,
44 _ => UsbGadgetState::Unknown,
45 }
46 }
47}
48
49impl UsbGadgetState {
50 pub fn to_fs_str(&self) -> &'static FsStr {
51 match self {
52 UsbGadgetState::Disconnected => b"DISCONNECTED".into(),
53 UsbGadgetState::Configured => b"CONFIGURED".into(),
54 UsbGadgetState::Connected => b"CONNECTED".into(),
55 UsbGadgetState::Unknown => b"UNKNOWN".into(),
56 }
57 }
58
59 pub fn map_from_device_state(state: DeviceState, previous: Option<Self>) -> Option<Self> {
60 match state {
61 DeviceState::NotAttached => Some(UsbGadgetState::Disconnected),
62 DeviceState::Attached => Some(UsbGadgetState::Connected),
63 DeviceState::Powered => Some(UsbGadgetState::Connected),
64 DeviceState::Default => Some(UsbGadgetState::Connected),
65 DeviceState::Address => Some(UsbGadgetState::Connected),
66 DeviceState::Configured => Some(UsbGadgetState::Configured),
67 DeviceState::Suspended => previous, _ => {
69 log_error!("Unexpected DeviceState: {:?}", state);
70 previous
71 }
72 }
73 }
74}
75
76#[derive(Clone)]
77struct UsbStateSysfsFile {
78 state: Arc<AtomicU8>,
79}
80
81impl BytesFileOps for UsbStateSysfsFile {
82 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
83 let state_val = self.state.load(Ordering::Relaxed);
84 let state = UsbGadgetState::from(state_val);
85 let mut content = state.to_fs_str().to_vec();
86 content.push(b'\n'); Ok(Cow::Owned(content))
88 }
89}
90
91pub fn usb_device_init(
92 locked: &mut Locked<Unlocked>,
93 kernel: &Arc<Kernel>,
94) -> Result<Device, Errno> {
95 let registry = &kernel.device_registry;
96
97 let android_usb_class =
98 registry.objects.get_or_create_class("android_usb".into(), registry.objects.virtual_bus());
99
100 let shared_state = Arc::new(AtomicU8::new(UsbGadgetState::Disconnected as u8));
101 let state_clone = shared_state.clone();
102
103 let device = registry.register_dyn_device_with_dir(
104 locked,
105 kernel,
106 "android0".into(),
107 android_usb_class,
108 |device, dir| {
109 build_device_directory(device, dir);
110 dir.entry(
111 "state",
112 BytesFile::new_node(UsbStateSysfsFile { state: state_clone }),
113 mode!(IFREG, 0o444),
114 );
115 },
116 simple_device_ops::<DevNull>,
117 )?;
118
119 let kernel_clone = Arc::clone(kernel);
120 let device_clone = device.clone();
121 kernel.kthreads.spawn_future(
122 move || async move {
123 monitor_usb_device_state(kernel_clone, device_clone, shared_state).await;
124 },
125 "usb_device_state_monitor",
126 );
127
128 Ok(device)
129}
130
131fn dispatch_usb_state_change(
133 kernel: &Arc<Kernel>,
134 device: &Device,
135 usb_state: FsString,
136) -> impl Future<Output = Result<(), Errno>> {
137 let spawner = kernel.kthreads.spawner();
138 let device_clone = device.clone();
139 let closure = move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
140 if let Some(metadata) = &device_clone.metadata {
141 metadata.properties.insert("USB_STATE".into(), usb_state);
142 }
143
144 current_task.kernel.device_registry.dispatch_uevent(
145 locked,
146 UEventAction::Change,
147 device_clone,
148 );
149 };
150 let (result, request) =
151 SpawnRequestBuilder::new().with_sync_closure(closure).build_with_async_result();
152 spawner.spawn_from_request(request);
153 result
154}
155
156async fn monitor_usb_device_state(
159 kernel: Arc<Kernel>,
160 device: Device,
161 shared_state: Arc<AtomicU8>,
162) {
163 let provider = connect_to_protocol::<PolicyProviderMarker>()
164 .expect("USB Failed to connect to PolicyProvider");
165 let mut previous_mapped_state: Option<UsbGadgetState> = None;
166 loop {
167 match provider.watch_device_state().await {
168 Ok(Ok(update)) => {
169 let state = update.state.unwrap_or_else(DeviceState::unknown);
170
171 let mapped = UsbGadgetState::map_from_device_state(state, previous_mapped_state);
173
174 if previous_mapped_state != mapped {
175 if let Some(val) = mapped {
176 previous_mapped_state = Some(val);
177 shared_state.store(val as u8, Ordering::Relaxed);
178 if let Err(e) =
179 dispatch_usb_state_change(&kernel, &device, val.to_fs_str().to_owned())
180 .await
181 {
182 log_error!("Failed to dispatch USB state change: {:?}", e);
183 }
184 }
185 }
186 }
187 Ok(Err(err)) => {
188 log_error!("USB PolicyProvider returned error: {:?}", err);
189 break;
190 }
191 Err(e) => {
192 log_error!("USB PolicyProvider watch failed: {:?}", e);
193 break;
194 }
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use starnix_core::testing::spawn_kernel_and_run;
203 use starnix_rcu::RcuReadScope;
204
205 #[::fuchsia::test]
206 async fn test_usb_gadget_state_map_from_device_state() {
207 assert_eq!(
208 UsbGadgetState::map_from_device_state(DeviceState::NotAttached, None),
209 Some(UsbGadgetState::Disconnected)
210 );
211 assert_eq!(
212 UsbGadgetState::map_from_device_state(DeviceState::Configured, None),
213 Some(UsbGadgetState::Configured)
214 );
215 assert_eq!(
216 UsbGadgetState::map_from_device_state(DeviceState::Attached, None),
217 Some(UsbGadgetState::Connected)
218 );
219 assert_eq!(
220 UsbGadgetState::map_from_device_state(DeviceState::Powered, None),
221 Some(UsbGadgetState::Connected)
222 );
223 assert_eq!(
224 UsbGadgetState::map_from_device_state(DeviceState::Default, None),
225 Some(UsbGadgetState::Connected)
226 );
227 assert_eq!(
228 UsbGadgetState::map_from_device_state(DeviceState::Address, None),
229 Some(UsbGadgetState::Connected)
230 );
231 assert_eq!(
232 UsbGadgetState::map_from_device_state(
233 DeviceState::Suspended,
234 Some(UsbGadgetState::Configured)
235 ),
236 Some(UsbGadgetState::Configured)
237 );
238 assert_eq!(UsbGadgetState::map_from_device_state(DeviceState::Suspended, None), None);
239 assert_eq!(UsbGadgetState::map_from_device_state(DeviceState::unknown(), None), None);
240 assert_eq!(
241 UsbGadgetState::map_from_device_state(
242 DeviceState::unknown(),
243 Some(UsbGadgetState::Configured)
244 ),
245 Some(UsbGadgetState::Configured)
246 );
247 }
248
249 #[::fuchsia::test]
250 async fn test_usb_sysfs_state_reads() {
251 spawn_kernel_and_run(async |_locked, current_task| {
252 let shared_state = Arc::new(AtomicU8::new(UsbGadgetState::Disconnected as u8));
253
254 shared_state.store(UsbGadgetState::Configured as u8, Ordering::Relaxed);
255
256 let sysfs_file = UsbStateSysfsFile { state: shared_state };
257 let content = sysfs_file.read(current_task).expect("read failed");
258 assert_eq!(content.as_ref(), b"CONFIGURED\n");
259 })
260 .await;
261 }
262
263 #[::fuchsia::test]
264 async fn test_usb_device_state_to_fs_str() {
265 assert_eq!(UsbGadgetState::Disconnected.to_fs_str(), b"DISCONNECTED");
266 assert_eq!(UsbGadgetState::Configured.to_fs_str(), b"CONFIGURED");
267 assert_eq!(UsbGadgetState::Connected.to_fs_str(), b"CONNECTED");
268 assert_eq!(UsbGadgetState::Unknown.to_fs_str(), b"UNKNOWN");
269 }
270
271 #[::fuchsia::test]
272 async fn test_usb_device_state_from_u8() {
273 assert_eq!(UsbGadgetState::from(0), UsbGadgetState::Disconnected);
274 assert_eq!(UsbGadgetState::from(1), UsbGadgetState::Configured);
275 assert_eq!(UsbGadgetState::from(2), UsbGadgetState::Connected);
276 assert_eq!(UsbGadgetState::from(255), UsbGadgetState::Unknown);
277 }
278
279 #[::fuchsia::test]
280 async fn test_usb_device_init_sysfs() {
281 spawn_kernel_and_run(async |locked, current_task| {
282 let device =
283 usb_device_init(locked, current_task.kernel()).expect("usb_device_init failed");
284
285 assert_eq!(device.name.as_slice(), b"android0");
286 let metadata = device.metadata.as_ref().expect("metadata not found");
287 assert_eq!(metadata.devname.as_slice(), b"android0");
288 })
289 .await;
290 }
291
292 #[::fuchsia::test]
293 async fn test_usb_device_metadata_property_injection() {
294 spawn_kernel_and_run(async |locked, current_task| {
295 let device =
296 usb_device_init(locked, current_task.kernel()).expect("usb_device_init failed");
297
298 let kernel = current_task.kernel();
299
300 dispatch_usb_state_change(
302 kernel,
303 &device,
304 UsbGadgetState::Connected.to_fs_str().to_owned(),
305 )
306 .await
307 .unwrap();
308
309 let metadata = device.metadata.as_ref().expect("metadata not found");
310 let scope = RcuReadScope::new();
311 let value = metadata
312 .properties
313 .get(&scope, FsStr::new(b"USB_STATE"))
314 .expect("property not found");
315 assert_eq!(value.as_slice(), b"CONNECTED");
316 })
317 .await;
318 }
319}