Skip to main content

custom_interface_device_split/
custom_interface_device_split.rs

1//! Device-side example for USB gadget with custom interface.
2//! This example follows the custom_interface_device.rs, but also
3//! demonstrates how it is possible to run the privileged parts of
4//! gadget setup (interacting with ConfigFS) in a different process to
5//! the custom function parts (FunctionFS). This example runs
6//! the main gadget logic in an unprivileged process.
7
8use bytes::BytesMut;
9use std::{
10    io::ErrorKind,
11    sync::{
12        atomic::{AtomicBool, Ordering},
13        Arc,
14    },
15    thread,
16    time::Duration,
17};
18
19use usb_gadget::{
20    default_udc,
21    function::custom::{Custom, Endpoint, EndpointDirection, EndpointReceiver, EndpointSender, Event, Interface},
22    Class, Config, Gadget, Id, OsDescriptor, Strings, WebUsb,
23};
24
25fn main() {
26    env_logger::init();
27
28    let existing = std::env::var("EXISTING_FFS").ok();
29    let register_only = std::env::var("REGISTER_ONLY").ok().is_some();
30
31    let (ep1_rx, ep1_dir) = EndpointDirection::host_to_device();
32    let (ep2_tx, ep2_dir) = EndpointDirection::device_to_host();
33
34    let mut builder = Custom::builder();
35
36    if register_only {
37        // We are only registering and binding the gadget, and leaving the FunctionFS interactions
38        // to another process.
39        builder.ffs_no_init = true;
40        builder.ffs_uid = Some(std::env::var("SUDO_UID").unwrap().parse().unwrap());
41        builder.ffs_gid = Some(std::env::var("SUDO_GID").unwrap().parse().unwrap());
42    } else {
43        builder = builder.with_interface(
44            Interface::new(Class::vendor_specific(1, 2), "custom interface")
45                .with_endpoint(Endpoint::bulk(ep1_dir))
46                .with_endpoint(Endpoint::bulk(ep2_dir)),
47        );
48    }
49
50    let (reg, custom) = if let Some(ref path) = existing {
51        (None, builder.existing(path).unwrap())
52    } else {
53        let (mut custom, handle) = builder.build();
54
55        usb_gadget::remove_all().expect("cannot remove all gadgets");
56
57        let udc = default_udc().expect("cannot get UDC");
58        let gadget = Gadget::new(
59            Class::vendor_specific(255, 3),
60            Id::new(6, 0x11),
61            Strings::new("manufacturer", "custom USB interface", "serial_number"),
62        )
63        .with_config(Config::new("config").with_function(handle))
64        .with_os_descriptor(OsDescriptor::microsoft())
65        .with_web_usb(WebUsb::new(0xf1, "http://webusb.org"));
66
67        let reg = gadget.register().expect("cannot register gadget");
68
69        if register_only {
70            let ffs_dir = custom.ffs_dir().unwrap();
71            println!("FunctionFS dir mounted at {}", ffs_dir.display());
72            println!("You can now run this program again as unprivileged user:");
73            println!("EXISTING_FFS={} {}", ffs_dir.display(), std::env::args().next().unwrap());
74
75            let mut ep1_path = ffs_dir.clone();
76            ep1_path.push("ep1");
77            while std::fs::metadata(&ep1_path).is_err() {
78                thread::sleep(Duration::from_secs(1));
79            }
80
81            println!("Detected ep1 in FunctionFS dir, this means descriptors have been written to ep0.");
82            println!("Now binding gadget to UDC (making it active)...");
83        }
84
85        reg.bind(Some(&udc)).expect("cannot bind to UDC");
86
87        println!("Custom function at {}", custom.status().unwrap().path().unwrap().display());
88        println!();
89
90        (Some(reg), custom)
91    };
92
93    if register_only {
94        println!("Waiting for the gadget to become unbound. If you stop the other process, this will happen automatically.");
95        while reg.as_ref().unwrap().udc().unwrap().is_some() {
96            thread::sleep(Duration::from_secs(1));
97        }
98    } else {
99        if existing.is_some() {
100            println!("The FunctionFS setup is done, you can type 'yes' in the other process and hit <ENTER>");
101        }
102        run(ep1_rx, ep2_tx, custom);
103    }
104
105    if let Some(reg) = reg {
106        println!("Unregistering");
107        reg.remove().unwrap();
108    }
109}
110
111fn run(mut ep1_rx: EndpointReceiver, mut ep2_tx: EndpointSender, mut custom: Custom) {
112    let ep1_control = ep1_rx.control().unwrap();
113    println!("ep1 unclaimed: {:?}", ep1_control.unclaimed_fifo());
114    println!("ep1 real address: {}", ep1_control.real_address().unwrap());
115    println!("ep1 descriptor: {:?}", ep1_control.descriptor().unwrap());
116    println!();
117
118    let ep2_control = ep2_tx.control().unwrap();
119    println!("ep2 unclaimed: {:?}", ep2_control.unclaimed_fifo());
120    println!("ep2 real address: {}", ep2_control.real_address().unwrap());
121    println!("ep2 descriptor: {:?}", ep2_control.descriptor().unwrap());
122    println!();
123
124    let stop = Arc::new(AtomicBool::new(false));
125
126    thread::scope(|s| {
127        s.spawn(|| {
128            let size = ep1_rx.max_packet_size().unwrap();
129            let mut b = 0;
130            while !stop.load(Ordering::Relaxed) {
131                let data = ep1_rx
132                    .recv_timeout(BytesMut::with_capacity(size), Duration::from_secs(1))
133                    .expect("recv failed");
134                match data {
135                    Some(data) => {
136                        println!("received {} bytes: {data:x?}", data.len());
137                        if !data.iter().all(|x| *x == b) {
138                            panic!("wrong data received");
139                        }
140                        b = b.wrapping_add(1);
141                    }
142                    None => {
143                        println!("receive empty");
144                    }
145                }
146            }
147        });
148
149        s.spawn(|| {
150            let size = ep2_tx.max_packet_size().unwrap();
151            let mut b = 0u8;
152            while !stop.load(Ordering::Relaxed) {
153                let data = vec![b; size];
154                match ep2_tx.send_timeout(data.into(), Duration::from_secs(1)) {
155                    Ok(()) => {
156                        println!("sent data {b} of size {size} bytes");
157                        b = b.wrapping_add(1);
158                    }
159                    Err(err) if err.kind() == ErrorKind::TimedOut => println!("send timeout"),
160                    Err(err) => panic!("send failed: {err}"),
161                }
162            }
163        });
164
165        s.spawn(|| {
166            let mut ctrl_data = Vec::new();
167
168            while !stop.load(Ordering::Relaxed) {
169                if let Some(event) = custom.event_timeout(Duration::from_secs(1)).expect("event failed") {
170                    println!("Event: {event:?}");
171                    match event {
172                        Event::SetupHostToDevice(req) => {
173                            if req.ctrl_req().request == 255 {
174                                println!("Stopping");
175                                stop.store(true, Ordering::Relaxed);
176                            }
177                            ctrl_data = req.recv_all().unwrap();
178                            println!("Control data: {ctrl_data:x?}");
179                        }
180                        Event::SetupDeviceToHost(req) => {
181                            println!("Replying with data");
182                            req.send(&ctrl_data).unwrap();
183                        }
184                        _ => (),
185                    }
186                } else {
187                    println!("no event");
188                }
189            }
190        });
191    });
192}