starnix_modules_nanohub/
nanohub.rs1use crate::datachannel_file::DataChannelDevice;
6use crate::nanohub_comms_directory::{
7 build_display_comms_directory, build_nanohub_comms_directory,
8};
9use crate::socket_tunnel_file::register_socket_tunnel_device;
10use fuchsia_component::client::Service;
11use futures::TryStreamExt;
12use starnix_core::device::serial::SerialDevice;
13use starnix_core::fs::sysfs::build_device_directory;
14use starnix_core::task::{CurrentTask, Kernel};
15use starnix_logging::{log_error, log_info, log_warn};
16use starnix_sync::{Locked, Unlocked};
17use std::ops::DerefMut;
18use std::sync::Arc;
19use {fidl_fuchsia_hardware_google_nanohub as fnanohub, fidl_fuchsia_hardware_serial as fserial};
20
21const SERIAL_DIRECTORY: &str = "/dev/class/serial";
22
23pub fn nanohub_device_init(locked: &mut Locked<Unlocked>, current_task: &CurrentTask) {
24 register_socket_tunnel_device(
25 locked,
26 current_task,
27 "/dev/display_comms".into(),
28 "display_comms".into(),
29 "display".into(),
30 build_display_comms_directory,
31 );
32
33 register_socket_tunnel_device(
36 locked,
37 current_task,
38 "/dev/nanohub_comms".into(),
39 "nanohub_comms".into(),
40 "nanohub".into(),
41 build_nanohub_comms_directory,
42 );
43
44 current_task.kernel().kthreads.spawn_future(
46 {
47 let kernel = current_task.kernel().clone();
48 move || async move { register_serial_device(kernel).await }
49 },
50 "register_serial_device",
51 );
52
53 current_task.kernel().kthreads.spawn_future(
54 {
55 let kernel = current_task.kernel().clone();
56 move || async move { register_datachannel_devices(kernel).await }
57 },
58 "register_datachannel_devices",
59 );
60}
61
62async fn register_datachannel_devices(kernel: Arc<Kernel>) {
63 let current_task = kernel.kthreads.system_task();
64 let service = match Service::open(fnanohub::StarnixDataChannelServiceMarker) {
65 Ok(service) => service,
66 Err(e) => {
67 log_warn!("Failed to open DriverService: {:?}", e);
68 return;
69 }
70 };
71 let mut watcher = match service.watch().await {
72 Ok(watcher) => watcher,
73 Err(e) => {
74 log_warn!("Failed to create watcher: {:?}", e);
75 return;
76 }
77 };
78
79 while let Ok(Some(data_channel_service_proxy)) = watcher.try_next().await {
80 let name = match (|| {
81 let device_proxy = data_channel_service_proxy.connect_to_waitable_sync()?;
82 let id = device_proxy.get_identifier(zx::MonotonicInstant::INFINITE)?;
83 Ok::<std::option::Option<std::string::String>, fidl::Error>(id.name)
84 })() {
85 Ok(Some(name)) => name,
86 Ok(None) => {
87 log_error!("Data channel device has no name, skipping registration");
88 continue;
89 }
90 Err(e) => {
91 log_error!("Failed to get device info: {:?}", e);
92 continue;
93 }
94 };
95
96 let registry = &kernel.device_registry;
97
98 let device_class =
99 registry.objects.get_or_create_class("nanohub".into(), registry.objects.virtual_bus());
100
101 if let Err(e) = registry.register_dyn_device_with_dir(
102 current_task.kernel().kthreads.unlocked_for_async().deref_mut(),
103 current_task,
104 name.as_bytes().into(),
105 device_class,
106 build_device_directory,
107 DataChannelDevice::new(
108 data_channel_service_proxy,
109 current_task.kernel().suspend_resume_manager.clone(),
110 ),
111 ) {
112 log_warn!("Failed to register datachannel device: {:?}", e);
113 }
114 }
115}
116
117async fn register_serial_device(kernel: Arc<Kernel>) {
118 let dir =
120 match fuchsia_fs::directory::open_in_namespace(SERIAL_DIRECTORY, fuchsia_fs::PERM_READABLE)
121 {
122 Ok(dir) => dir,
123 Err(e) => {
124 log_error!("Failed to open serial directory: {:}", e);
125 return;
126 }
127 };
128
129 let mut watcher = match fuchsia_fs::directory::Watcher::new(&dir).await {
130 Ok(watcher) => watcher,
131 Err(e) => {
132 log_info!("Failed to create directory watcher for serial device: {:}", e);
133 return;
134 }
135 };
136
137 loop {
138 match watcher.try_next().await {
139 Ok(Some(watch_msg)) => {
140 let current_task = kernel.kthreads.system_task();
141 let mut locked = kernel.kthreads.unlocked_for_async();
142 let locked = &mut locked;
143 let filename = watch_msg
144 .filename
145 .as_path()
146 .to_str()
147 .expect("Failed to convert watch_msg to str");
148 if filename == "." {
149 continue;
150 }
151 if watch_msg.event == fuchsia_fs::directory::WatchEvent::ADD_FILE
152 || watch_msg.event == fuchsia_fs::directory::WatchEvent::EXISTING
153 {
154 let instance_path = format!("{}/{}", SERIAL_DIRECTORY, filename);
155 let (client_channel, server_channel) = zx::Channel::create();
156 if let Err(_) = fdio::service_connect(&instance_path, server_channel) {
157 continue;
158 }
159
160 let device_proxy = fserial::DeviceProxy_SynchronousProxy::new(client_channel);
164 let (serial_proxy, server_end) =
165 fidl::endpoints::create_sync_proxy::<fserial::DeviceMarker>();
166
167 if let Err(_) = device_proxy.get_channel(server_end) {
169 continue;
170 }
171
172 let device_class = match serial_proxy.get_class(zx::MonotonicInstant::INFINITE)
174 {
175 Ok(class) => class,
176 Err(_) => continue,
177 };
178
179 if device_class == fserial::Class::Mcu {
180 let serial_device = SerialDevice::new(
181 locked,
182 current_task,
183 serial_proxy.into_channel().into(),
184 )
185 .expect("Can create SerialDevice wrapper");
186
187 let registry = ¤t_task.kernel().device_registry;
191 registry
192 .register_dyn_device(
193 locked,
194 current_task,
195 "ttyHS1".into(),
196 registry.objects.tty_class(),
197 serial_device,
198 )
199 .expect("Can register serial device");
200 break;
201 }
202 }
203 }
204 Ok(None) => {
205 break;
206 }
207 Err(e) => {
208 log_error!("Serial driver stream ended with error: {:}", e);
209 break;
210 }
211 }
212 }
213}