2
2
// Use of this source code is governed by a BSD-style license that can be
3
3
// found in the LICENSE file.
4
4
5
+ use std:: ops:: DerefMut ;
6
+ use std:: sync:: Arc ;
7
+
5
8
use crate :: nanohub_comms_directory:: NanohubCommsDirectory ;
6
9
use crate :: socket_tunnel_file:: register_socket_tunnel_device;
10
+ use fidl_fuchsia_hardware_serial as fserial;
11
+ use futures:: TryStreamExt ;
12
+ use starnix_core:: device:: serial:: SerialDevice ;
7
13
use starnix_core:: fs:: sysfs:: DeviceDirectory ;
8
- use starnix_core:: task:: CurrentTask ;
14
+ use starnix_core:: task:: { CurrentTask , Kernel } ;
9
15
use starnix_core:: vfs:: { BytesFile , FsString , StaticDirectoryBuilder } ;
10
- use starnix_sync:: { FileOpsCore , LockBefore , Locked } ;
16
+ use starnix_logging:: { log_error, log_info} ;
17
+ use starnix_sync:: { Locked , Unlocked } ;
11
18
use starnix_uapi:: mode;
12
19
20
+ const SERIAL_DIRECTORY : & str = "/dev/class/serial" ;
21
+
13
22
/// Function to be invoked by ProcDirectory while constructing /proc/device-tree
14
23
pub fn nanohub_procfs_builder (
15
24
builder : & ' _ mut StaticDirectoryBuilder < ' _ > ,
@@ -25,10 +34,7 @@ pub fn nanohub_procfs_builder(
25
34
} ) ;
26
35
}
27
36
28
- pub fn nanohub_device_init < L > ( locked : & mut Locked < ' _ , L > , current_task : & CurrentTask )
29
- where
30
- L : LockBefore < FileOpsCore > ,
31
- {
37
+ pub fn nanohub_device_init ( locked : & mut Locked < ' _ , Unlocked > , current_task : & CurrentTask ) {
32
38
struct Descriptor {
33
39
socket_label : FsString ,
34
40
dev_node_name : FsString ,
@@ -100,4 +106,105 @@ where
100
106
"nanohub" . into ( ) ,
101
107
NanohubCommsDirectory :: new,
102
108
) ;
109
+
110
+ // Spawn future to bind and configure serial device
111
+ current_task. kernel ( ) . kthreads . spawn_future ( {
112
+ let kernel = current_task. kernel ( ) . clone ( ) ;
113
+ async move { register_serial_device ( kernel) . await }
114
+ } ) ;
115
+ }
116
+
117
+ async fn register_serial_device ( kernel : Arc < Kernel > ) {
118
+ let current_task = kernel. kthreads . system_task ( ) ;
119
+
120
+ // TODO Move this to expect once test support is enabled
121
+ let dir =
122
+ match fuchsia_fs:: directory:: open_in_namespace ( SERIAL_DIRECTORY , fuchsia_fs:: PERM_READABLE )
123
+ {
124
+ Ok ( dir) => dir,
125
+ Err ( e) => {
126
+ log_error ! ( "Failed to open serial directory: {:}" , e) ;
127
+ return ;
128
+ }
129
+ } ;
130
+
131
+ let mut watcher = match fuchsia_fs:: directory:: Watcher :: new ( & dir) . await {
132
+ Ok ( watcher) => watcher,
133
+ Err ( e) => {
134
+ log_info ! ( "Failed to create directory watcher for serial device: {:}" , e) ;
135
+ return ;
136
+ }
137
+ } ;
138
+
139
+ loop {
140
+ match watcher. try_next ( ) . await {
141
+ Ok ( Some ( watch_msg) ) => {
142
+ let filename = watch_msg
143
+ . filename
144
+ . as_path ( )
145
+ . to_str ( )
146
+ . expect ( "Failed to convert watch_msg to str" ) ;
147
+ if filename == "." {
148
+ continue ;
149
+ }
150
+ if watch_msg. event == fuchsia_fs:: directory:: WatchEvent :: ADD_FILE
151
+ || watch_msg. event == fuchsia_fs:: directory:: WatchEvent :: EXISTING
152
+ {
153
+ let instance_path = format ! ( "{}/{}" , SERIAL_DIRECTORY , filename) ;
154
+ let ( client_channel, server_channel) = zx:: Channel :: create ( ) ;
155
+ if let Err ( _) = fdio:: service_connect ( & instance_path, server_channel) {
156
+ continue ;
157
+ }
158
+
159
+ // `fuchsia.hardware.serial` exposes a `DeviceProxy` type used for binding with
160
+ // a `Device` type. This should not be confused with the `DeviceProxy` generated
161
+ // by FIDL
162
+ let device_proxy = fserial:: DeviceProxy_SynchronousProxy :: new ( client_channel) ;
163
+ let ( serial_proxy, server_end) =
164
+ fidl:: endpoints:: create_sync_proxy :: < fserial:: DeviceMarker > ( ) ;
165
+
166
+ // Instruct the serial driver to bind the connection to the underlying device
167
+ if let Err ( _) = device_proxy. get_channel ( server_end) {
168
+ continue ;
169
+ }
170
+
171
+ // Fetch the device class to see if this is the correct instance
172
+ let device_class = match serial_proxy. get_class ( zx:: MonotonicInstant :: INFINITE )
173
+ {
174
+ Ok ( class) => class,
175
+ Err ( _) => continue ,
176
+ } ;
177
+
178
+ if device_class == fserial:: Class :: Mcu {
179
+ let serial_device =
180
+ SerialDevice :: new ( current_task, serial_proxy. into_channel ( ) . into ( ) )
181
+ . expect ( "Can create SerialDevice wrapper" ) ;
182
+
183
+ // TODO This will register with an incorrect device number. We should be
184
+ // dynamically registering a major device and this should be minor device 1
185
+ // of that major device.
186
+ let registry = & current_task. kernel ( ) . device_registry ;
187
+ registry
188
+ . register_dyn_device (
189
+ current_task. kernel ( ) . kthreads . unlocked_for_async ( ) . deref_mut ( ) ,
190
+ current_task,
191
+ "ttyHS1" . into ( ) ,
192
+ registry. objects . tty_class ( ) ,
193
+ DeviceDirectory :: new,
194
+ serial_device,
195
+ )
196
+ . expect ( "Can register serial device" ) ;
197
+ break ;
198
+ }
199
+ }
200
+ }
201
+ Ok ( None ) => {
202
+ break ;
203
+ }
204
+ Err ( e) => {
205
+ log_error ! ( "Serial driver stream ended with error: {:}" , e) ;
206
+ break ;
207
+ }
208
+ }
209
+ }
103
210
}
0 commit comments