Skip to main content

usb_gadget/
gadget.rs

1//! USB gadget.
2
3use rustix::io::Errno;
4use std::{
5    collections::HashMap,
6    ffi::{OsStr, OsString},
7    fmt, fs,
8    io::{Error, ErrorKind, Result},
9    os::unix::{
10        fs::symlink,
11        prelude::{OsStrExt, OsStringExt},
12    },
13    path::{Path, PathBuf},
14};
15
16use crate::{
17    configfs_dir, function,
18    function::{
19        util::{call_remove_handler, init_remove_handlers},
20        Handle,
21    },
22    hex_u16, hex_u8,
23    lang::Language,
24    request_module, trim_os_str,
25    udc::Udc,
26    Speed,
27};
28
29/// USB gadget ioctl magic byte.
30pub const GADGET_IOC_MAGIC: u8 = b'g';
31
32/// USB gadget or interface class.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct Class {
35    /// Class code.
36    pub class: u8,
37    /// Subclass code.
38    pub sub_class: u8,
39    /// Protocol code.
40    pub protocol: u8,
41}
42
43impl Class {
44    /// Vendor specific class code.
45    pub const VENDOR_SPECIFIC: u8 = 0xff;
46
47    /// Creates a new USB device or interface class.
48    pub const fn new(class: u8, sub_class: u8, protocol: u8) -> Self {
49        Self { class, sub_class, protocol }
50    }
51
52    /// Creates a new USB device or interface class with vendor-specific class code.
53    pub const fn vendor_specific(sub_class: u8, protocol: u8) -> Self {
54        Self::new(Self::VENDOR_SPECIFIC, sub_class, protocol)
55    }
56
57    /// Indicates that class information should be determined from the interface descriptors in the
58    /// device.
59    ///
60    /// Can only be used as device class.
61    ///
62    /// Use [`INTERFACE_SPECIFIC`](Self::INTERFACE_SPECIFIC) instead.
63    #[deprecated(since = "1.1.0", note = "use Class::INTERFACE_SPECIFIC instead")]
64    pub const fn interface_specific() -> Self {
65        Self::new(0, 0, 0)
66    }
67
68    // --- Device-level classes ---
69
70    /// Class information determined from interface descriptors.
71    ///
72    /// This is the most common device-level class and can only be used as device class.
73    pub const INTERFACE_SPECIFIC: Self = Self::new(0x00, 0x00, 0x00);
74
75    /// Miscellaneous device with Interface Association Descriptor (IAD).
76    ///
77    /// Used as device class when the gadget has multiple functions grouped via IADs
78    /// (e.g. composite devices with CDC + other functions, or UVC devices).
79    pub const MISCELLANEOUS_IAD: Self = Self::new(0xEF, 0x02, 0x01);
80
81    // --- Audio (class 0x01) ---
82
83    /// Audio control interface.
84    pub const AUDIO_CONTROL: Self = Self::new(0x01, 0x01, 0x00);
85    /// Audio streaming interface.
86    pub const AUDIO_STREAMING: Self = Self::new(0x01, 0x02, 0x00);
87    /// MIDI streaming interface.
88    pub const AUDIO_MIDISTREAMING: Self = Self::new(0x01, 0x03, 0x00);
89
90    // --- CDC (class 0x02 / 0x0A) ---
91
92    /// Communications Device Class, Abstract Control Model (modem/serial).
93    pub const CDC_ACM: Self = Self::new(0x02, 0x02, 0x01);
94    /// CDC Ethernet Networking Control Model.
95    pub const CDC_ECM: Self = Self::new(0x02, 0x06, 0x00);
96    /// CDC Ethernet Emulation Model.
97    pub const CDC_EEM: Self = Self::new(0x02, 0x0C, 0x07);
98    /// CDC Network Control Model.
99    pub const CDC_NCM: Self = Self::new(0x02, 0x0D, 0x00);
100    /// CDC Data interface.
101    pub const CDC_DATA: Self = Self::new(0x0A, 0x00, 0x00);
102
103    // --- HID (class 0x03) ---
104
105    /// Human Interface Device, no boot protocol.
106    pub const HID: Self = Self::new(0x03, 0x00, 0x00);
107    /// HID boot interface, keyboard protocol.
108    pub const HID_BOOT_KEYBOARD: Self = Self::new(0x03, 0x01, 0x01);
109    /// HID boot interface, mouse protocol.
110    pub const HID_BOOT_MOUSE: Self = Self::new(0x03, 0x01, 0x02);
111
112    // --- Printer (class 0x07) ---
113
114    /// Printer, unidirectional.
115    pub const PRINTER_UNIDIRECTIONAL: Self = Self::new(0x07, 0x01, 0x01);
116    /// Printer, bidirectional.
117    pub const PRINTER_BIDIRECTIONAL: Self = Self::new(0x07, 0x01, 0x02);
118
119    // --- Mass Storage (class 0x08) ---
120
121    /// Mass storage, SCSI transparent command set, bulk-only transport.
122    pub const MASS_STORAGE_SCSI_BULK: Self = Self::new(0x08, 0x06, 0x50);
123
124    // --- Video (class 0x0E) ---
125
126    /// Video control interface.
127    pub const VIDEO_CONTROL: Self = Self::new(0x0E, 0x01, 0x00);
128    /// Video streaming interface.
129    pub const VIDEO_STREAMING: Self = Self::new(0x0E, 0x02, 0x00);
130
131    // --- Application-specific (class 0xFE) ---
132
133    /// Device Firmware Upgrade (DFU), runtime mode.
134    pub const DFU_RUNTIME: Self = Self::new(0xFE, 0x01, 0x01);
135    /// Device Firmware Upgrade (DFU), DFU mode.
136    pub const DFU_MODE: Self = Self::new(0xFE, 0x01, 0x02);
137}
138
139/// USB gadget id.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
141pub struct Id {
142    /// Vendor id.
143    pub vendor: u16,
144    /// Product id.
145    pub product: u16,
146}
147
148impl Id {
149    /// Creates a new USB device id.
150    pub const fn new(vendor: u16, product: u16) -> Self {
151        Self { vendor, product }
152    }
153
154    /// Linux Foundation vendor id.
155    ///
156    /// Intended for development and testing of Linux USB gadgets.
157    /// For production devices, register your own vendor id with the USB-IF
158    /// or obtain one from <https://pid.codes>.
159    pub const LINUX_FOUNDATION_VID: u16 = 0x1d6b;
160
161    /// Linux Foundation serial gadget (CDC ACM).
162    ///
163    /// Intended for development and testing only.
164    pub const LINUX_FOUNDATION_SERIAL: Self = Self::new(0x1d6b, 0x0101);
165    /// Linux Foundation ethernet gadget (RNDIS/CDC).
166    ///
167    /// Intended for development and testing only.
168    pub const LINUX_FOUNDATION_ETHERNET: Self = Self::new(0x1d6b, 0x0102);
169    /// Linux Foundation file-backed storage gadget.
170    ///
171    /// Intended for development and testing only.
172    pub const LINUX_FOUNDATION_STORAGE: Self = Self::new(0x1d6b, 0x0103);
173    /// Linux Foundation multifunction composite gadget.
174    ///
175    /// Intended for development and testing only.
176    pub const LINUX_FOUNDATION_COMPOSITE: Self = Self::new(0x1d6b, 0x0104);
177    /// Linux Foundation FunctionFS gadget.
178    ///
179    /// Intended for development and testing only.
180    pub const LINUX_FOUNDATION_FFS: Self = Self::new(0x1d6b, 0x0105);
181}
182
183/// USB gadget description strings.
184#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
185pub struct Strings {
186    /// Manufacturer name.
187    pub manufacturer: String,
188    /// Product name.
189    pub product: String,
190    /// Serial number.
191    pub serial_number: String,
192}
193
194impl Strings {
195    /// Creates new USB device strings.
196    pub fn new(manufacturer: impl AsRef<str>, product: impl AsRef<str>, serial_number: impl AsRef<str>) -> Self {
197        Self {
198            manufacturer: manufacturer.as_ref().to_string(),
199            product: product.as_ref().to_string(),
200            serial_number: serial_number.as_ref().to_string(),
201        }
202    }
203}
204
205/// USB gadget operating system descriptor.
206#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
207pub struct OsDescriptor {
208    /// Vendor code for requests.
209    pub vendor_code: u8,
210    /// Signature.
211    pub qw_sign: String,
212    /// Index of configuration in [`Gadget::configs`] to be reported at index 0.
213    ///
214    /// Hosts which expect the "OS Descriptors" ask only for configurations at index 0,
215    /// but Linux-based USB devices can provide more than one configuration.
216    pub config: usize,
217}
218
219impl OsDescriptor {
220    /// Creates a new instance.
221    pub const fn new(vendor_code: u8, qw_sign: String) -> Self {
222        Self { vendor_code, qw_sign, config: 0 }
223    }
224
225    /// The Microsoft OS descriptor.
226    ///
227    /// Uses vendor code 0xf0 for requests.
228    pub fn microsoft() -> Self {
229        Self { vendor_code: 0xf0, qw_sign: "MSFT100".to_string(), config: 0 }
230    }
231}
232
233/// WebUSB version.
234#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
235pub enum WebUsbVersion {
236    /// Version 1.0
237    #[default]
238    V10,
239    /// Other version in BCD format.
240    Other(u16),
241}
242
243impl From<WebUsbVersion> for u16 {
244    fn from(value: WebUsbVersion) -> Self {
245        match value {
246            WebUsbVersion::V10 => 0x0100,
247            WebUsbVersion::Other(ver) => ver,
248        }
249    }
250}
251
252/// USB gadget WebUSB descriptor.
253#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
254pub struct WebUsb {
255    /// WebUSB specification version number.
256    pub version: WebUsbVersion,
257    /// bRequest value used for issuing WebUSB requests.
258    pub vendor_code: u8,
259    /// URL of the device's landing page.
260    pub landing_page: String,
261}
262
263impl WebUsb {
264    /// Creates a new instance.
265    pub fn new(vendor_code: u8, landing_page: impl AsRef<str>) -> Self {
266        Self { version: WebUsbVersion::default(), vendor_code, landing_page: landing_page.as_ref().to_string() }
267    }
268}
269
270/// USB gadget configuration.
271#[derive(Debug, Clone)]
272#[non_exhaustive]
273pub struct Config {
274    /// Maximum power in mA.
275    pub max_power: u16,
276    /// Self powered?
277    pub self_powered: bool,
278    /// Remote wakeup?
279    pub remote_wakeup: bool,
280    /// Configuration description string.
281    pub description: HashMap<Language, String>,
282    /// Functions, i.e. USB interfaces, present in this configuration.
283    pub functions: Vec<function::Handle>,
284}
285
286impl Config {
287    /// Creates a new USB gadget configuration.
288    pub fn new(description: impl AsRef<str>) -> Self {
289        Self {
290            max_power: 500,
291            self_powered: false,
292            remote_wakeup: false,
293            description: [(Language::default(), description.as_ref().to_string())].into(),
294            functions: Default::default(),
295        }
296    }
297
298    /// Adds a USB function (interface) to this configuration.
299    pub fn add_function(&mut self, function_handle: function::Handle) {
300        self.functions.push(function_handle);
301    }
302
303    /// Adds a USB function (interface) to this configuration.
304    #[must_use]
305    pub fn with_function(mut self, function_handle: function::Handle) -> Self {
306        self.add_function(function_handle);
307        self
308    }
309
310    fn register(
311        &self, gadget_dir: &Path, idx: usize, func_dirs: &HashMap<function::Handle, PathBuf>,
312    ) -> Result<PathBuf> {
313        let dir = gadget_dir.join("configs").join(format!("c.{idx}"));
314        log::debug!("creating config at {}", dir.display());
315        fs::create_dir(&dir)?;
316
317        let mut attributes = 1 << 7;
318        if self.self_powered {
319            attributes |= 1 << 6;
320        }
321        if self.remote_wakeup {
322            attributes |= 1 << 5;
323        }
324
325        fs::write(dir.join("bmAttributes"), hex_u8(attributes))?;
326        fs::write(dir.join("MaxPower"), self.max_power.to_string())?;
327
328        for (&lang, desc) in &self.description {
329            let lang_dir = dir.join("strings").join(hex_u16(lang.into()));
330            fs::create_dir(&lang_dir)?;
331            fs::write(lang_dir.join("configuration"), desc)?;
332        }
333
334        for func in &self.functions {
335            let func_dir = &func_dirs[func];
336            log::debug!("adding function {}", func_dir.display());
337            symlink(func_dir, dir.join(func_dir.file_name().unwrap()))?;
338        }
339
340        Ok(dir)
341    }
342}
343
344/// USB version.
345#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
346pub enum UsbVersion {
347    /// USB 1.1
348    V11,
349    /// USB 2.0
350    #[default]
351    V20,
352    /// USB 2.0 with BOS descriptor support (bcdUSB 0x0201), needed for WinUSB.
353    V21,
354    /// USB 3.0
355    V30,
356    /// USB 3.1
357    V31,
358    /// Other version in BCD format.
359    Other(u16),
360}
361
362impl From<UsbVersion> for u16 {
363    fn from(value: UsbVersion) -> Self {
364        match value {
365            UsbVersion::V11 => 0x0110,
366            UsbVersion::V20 => 0x0200,
367            UsbVersion::V21 => 0x0201,
368            UsbVersion::V30 => 0x0300,
369            UsbVersion::V31 => 0x0310,
370            UsbVersion::Other(ver) => ver,
371        }
372    }
373}
374
375/// USB gadget definition.
376///
377/// Fields set to `None` are left at their kernel-provided default values.
378#[derive(Debug, Clone)]
379#[non_exhaustive]
380pub struct Gadget {
381    /// Custom name for the gadget in configfs.
382    ///
383    /// If `None`, an auto-generated name like `usb-gadget0` is used.
384    pub name: Option<String>,
385    /// USB device class.
386    pub device_class: Class,
387    /// USB device id.
388    pub id: Id,
389    /// USB device strings.
390    pub strings: HashMap<Language, Strings>,
391    /// Maximum endpoint 0 packet size.
392    pub max_packet_size0: u8,
393    /// Device release number in BCD format.
394    ///
395    /// No hexadecimal digit must exceed 9.
396    pub device_release: u16,
397    /// USB specification version.
398    pub usb_version: UsbVersion,
399    /// Maximum speed supported by driver.
400    pub max_speed: Option<Speed>,
401    /// OS descriptor extension.
402    pub os_descriptor: Option<OsDescriptor>,
403    /// WebUSB extension.
404    pub web_usb: Option<WebUsb>,
405    /// USB device configurations.
406    pub configs: Vec<Config>,
407}
408
409impl Gadget {
410    /// Creates a new USB gadget definition.
411    pub fn new(device_class: Class, id: Id, strings: Strings) -> Self {
412        Self {
413            name: None,
414            device_class,
415            id,
416            strings: [(Language::default(), strings)].into(),
417            max_packet_size0: 64,
418            device_release: 0x0000,
419            usb_version: UsbVersion::default(),
420            max_speed: None,
421            os_descriptor: None,
422            web_usb: None,
423            configs: Vec::new(),
424        }
425    }
426
427    /// Adds a USB device configuration.
428    pub fn add_config(&mut self, config: Config) {
429        self.configs.push(config);
430    }
431
432    /// Adds a USB device configuration.
433    #[must_use]
434    pub fn with_config(mut self, config: Config) -> Self {
435        self.add_config(config);
436        self
437    }
438
439    /// Sets the OS descriptor.
440    #[must_use]
441    pub fn with_os_descriptor(mut self, os_descriptor: OsDescriptor) -> Self {
442        self.os_descriptor = Some(os_descriptor);
443        self
444    }
445
446    /// Sets the WebUSB extension.
447    #[must_use]
448    pub fn with_web_usb(mut self, web_usb: WebUsb) -> Self {
449        self.web_usb = Some(web_usb);
450        self
451    }
452
453    /// Register the USB gadget.
454    ///
455    /// At least one [configuration](Config) must be added before the gadget
456    /// can be registered.
457    #[must_use = "consumes the gadget"]
458    pub fn register(self) -> Result<RegGadget> {
459        if self.configs.is_empty() {
460            return Err(Error::new(ErrorKind::InvalidInput, "USB gadget must have at least one configuration"));
461        }
462
463        let usb_gadget_dir = usb_gadget_dir()?;
464
465        let dir = if let Some(ref name) = self.name {
466            let dir = usb_gadget_dir.join(name);
467            fs::create_dir(&dir)?;
468            dir
469        } else {
470            let mut gadget_idx: u16 = 0;
471            loop {
472                let dir = usb_gadget_dir.join(format!("usb-gadget{gadget_idx}"));
473                match fs::create_dir(&dir) {
474                    Ok(()) => break dir,
475                    Err(err) if err.kind() == ErrorKind::AlreadyExists => (),
476                    Err(err) => return Err(err),
477                }
478                gadget_idx = gadget_idx
479                    .checked_add(1)
480                    .ok_or_else(|| Error::new(ErrorKind::OutOfMemory, "USB gadgets exhausted"))?;
481            }
482        };
483
484        log::debug!("registering gadget at {}", dir.display());
485
486        fs::write(dir.join("bDeviceClass"), hex_u8(self.device_class.class))?;
487        fs::write(dir.join("bDeviceSubClass"), hex_u8(self.device_class.sub_class))?;
488        fs::write(dir.join("bDeviceProtocol"), hex_u8(self.device_class.protocol))?;
489
490        fs::write(dir.join("idVendor"), hex_u16(self.id.vendor))?;
491        fs::write(dir.join("idProduct"), hex_u16(self.id.product))?;
492
493        fs::write(dir.join("bMaxPacketSize0"), hex_u8(self.max_packet_size0))?;
494        fs::write(dir.join("bcdDevice"), hex_u16(self.device_release))?;
495        fs::write(dir.join("bcdUSB"), hex_u16(self.usb_version.into()))?;
496
497        if let Some(v) = self.max_speed {
498            fs::write(dir.join("max_speed"), v.to_string())?;
499        }
500
501        if let Some(webusb) = &self.web_usb {
502            let webusb_dir = dir.join("webusb");
503            if webusb_dir.is_dir() {
504                fs::write(webusb_dir.join("bVendorCode"), hex_u8(webusb.vendor_code))?;
505                fs::write(webusb_dir.join("bcdVersion"), hex_u16(webusb.version.into()))?;
506                fs::write(webusb_dir.join("landingPage"), &webusb.landing_page)?;
507                fs::write(webusb_dir.join("use"), "1")?;
508            } else {
509                log::warn!("WebUSB descriptor is unsupported by kernel");
510            }
511        }
512
513        for (&lang, strs) in &self.strings {
514            let lang_dir = dir.join("strings").join(hex_u16(lang.into()));
515            fs::create_dir(&lang_dir)?;
516
517            fs::write(lang_dir.join("manufacturer"), &strs.manufacturer)?;
518            fs::write(lang_dir.join("product"), &strs.product)?;
519            fs::write(lang_dir.join("serialnumber"), &strs.serial_number)?;
520        }
521
522        let gadget_name = dir.file_name().unwrap().to_string_lossy();
523        let mut functions = Vec::new();
524        for func in self.configs.iter().flat_map(|c| &c.functions) {
525            if !functions.contains(&func) {
526                functions.push(func);
527            }
528        }
529        let mut func_dirs = HashMap::new();
530        for (func_idx, &func) in functions.iter().enumerate() {
531            let func_dir = dir.join(
532                dir.join("functions")
533                    .join(format!("{}.{gadget_name}-{func_idx}", func.get().driver().to_string_lossy())),
534            );
535            log::debug!("creating function at {}", func_dir.display());
536            fs::create_dir(&func_dir)?;
537
538            func.get().dir().set_dir(&func_dir);
539            func.get().register()?;
540
541            func_dirs.insert(func.clone(), func_dir);
542        }
543
544        let mut config_dirs = Vec::new();
545        for (idx, config) in self.configs.iter().enumerate() {
546            let dir = config.register(&dir, idx + 1, &func_dirs)?;
547            config_dirs.push(dir);
548        }
549
550        if let Some(os_desc) = &self.os_descriptor {
551            let os_desc_dir = dir.join("os_desc");
552            if os_desc_dir.is_dir() {
553                fs::write(os_desc_dir.join("b_vendor_code"), hex_u8(os_desc.vendor_code))?;
554                fs::write(os_desc_dir.join("qw_sign"), &os_desc.qw_sign)?;
555                fs::write(os_desc_dir.join("use"), "1")?;
556
557                let config_dir = config_dirs.get(os_desc.config).ok_or_else(|| {
558                    Error::new(ErrorKind::InvalidInput, "invalid configuration index in OS descriptor")
559                })?;
560                log::debug!("linking OS descriptor to config dir {}", config_dir.display());
561                symlink(config_dir, os_desc_dir.join(config_dir.file_name().unwrap()))?;
562            } else {
563                log::warn!("USB OS descriptor is unsupported by kernel");
564            }
565        }
566
567        log::debug!("gadget at {} registered", dir.display());
568        Ok(RegGadget { dir, attached: true, func_dirs })
569    }
570
571    /// Register and bind USB gadget to a USB device controller (UDC).
572    ///
573    /// At least one [configuration](Config) must be added before the gadget
574    /// can be bound.
575    #[must_use = "consumes the gadget"]
576    pub fn bind(self, udc: &Udc) -> Result<RegGadget> {
577        let reg = self.register()?;
578        reg.bind(Some(udc))?;
579        Ok(reg)
580    }
581}
582
583/// USB gadget registered with the system.
584///
585/// A function instance registered in a USB gadget.
586#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
587pub struct RegFunction {
588    /// Function driver name, e.g. "acm", "ecm", "mass_storage".
589    driver: String,
590    /// Instance name, e.g. "composite-0".
591    instance: String,
592}
593
594impl RegFunction {
595    /// Function driver name, e.g. "acm", "ecm", "mass_storage".
596    pub fn driver(&self) -> &str {
597        &self.driver
598    }
599
600    /// Instance name, e.g. "composite-0".
601    pub fn instance(&self) -> &str {
602        &self.instance
603    }
604}
605
606impl fmt::Display for RegFunction {
607    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
608        write!(f, "{}.{}", self.driver, self.instance)
609    }
610}
611
612/// If this was obtained by calling [`Gadget::bind`], the USB gadget will be
613/// unbound and removed when this is dropped.
614///
615/// Call [`registered`] to obtain all gadgets registered on the system.
616#[must_use = "The USB gadget is removed when RegGadget is dropped."]
617pub struct RegGadget {
618    dir: PathBuf,
619    attached: bool,
620    func_dirs: HashMap<Handle, PathBuf>,
621}
622
623impl fmt::Debug for RegGadget {
624    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
625        f.debug_struct("RegGadget").field("name", &self.name()).field("is_attached", &self.is_attached()).finish()
626    }
627}
628
629impl RegGadget {
630    /// Name of this USB gadget in configfs.
631    pub fn name(&self) -> &OsStr {
632        self.dir.file_name().unwrap()
633    }
634
635    /// Path of this USB gadget in configfs.
636    pub fn path(&self) -> &Path {
637        &self.dir
638    }
639
640    /// If true, the USB gadget will be removed when this is dropped.
641    pub fn is_attached(&self) -> bool {
642        self.attached
643    }
644
645    /// The name of the USB device controller (UDC) this gadget is bound to.
646    pub fn udc(&self) -> Result<Option<OsString>> {
647        let data = OsString::from_vec(fs::read(self.dir.join("UDC"))?);
648        let data = trim_os_str(&data);
649        if data.is_empty() {
650            Ok(None)
651        } else {
652            Ok(Some(data.to_os_string()))
653        }
654    }
655
656    /// Returns all function instances registered in this gadget.
657    pub fn functions(&self) -> Result<Vec<RegFunction>> {
658        let func_dir = self.dir.join("functions");
659        let mut functions = Vec::new();
660
661        if let Ok(entries) = fs::read_dir(&func_dir) {
662            for entry in entries {
663                let Ok(entry) = entry else { continue };
664                if !entry.metadata()?.is_dir() {
665                    continue;
666                }
667                let name = entry.file_name().to_string_lossy().to_string();
668                if let Some((driver, instance)) = name.split_once('.') {
669                    functions.push(RegFunction { driver: driver.to_string(), instance: instance.to_string() });
670                }
671            }
672        }
673
674        functions.sort();
675        Ok(functions)
676    }
677
678    /// Binds the gadget to the specified USB device controller (UDC).
679    ///
680    /// If `udc` is `None`, the gadget is unbound from any UDC.
681    pub fn bind(&self, udc: Option<&Udc>) -> Result<()> {
682        log::debug!("binding gadget {:?} to {:?}", self, &udc);
683
684        let name = match udc {
685            Some(udc) => udc.name().to_os_string(),
686            None => "\n".into(),
687        };
688
689        match fs::write(self.dir.join("UDC"), name.as_bytes()) {
690            Ok(()) => (),
691            Err(err) if udc.is_none() && err.raw_os_error() == Some(Errno::NODEV.raw_os_error()) => (),
692            Err(err) => return Err(err),
693        }
694
695        for func in self.func_dirs.keys() {
696            func.get().dir().set_bound(udc.is_some());
697        }
698
699        Ok(())
700    }
701
702    /// Detach the handle from the USB gadget while keeping the USB gadget active.
703    ///
704    /// This prevents the USB gadget from being deregistered when this handle is dropped.
705    pub fn detach(&mut self) {
706        self.attached = false;
707    }
708
709    fn do_remove(&mut self) -> Result<()> {
710        for func in self.func_dirs.keys() {
711            func.get().pre_removal()?;
712        }
713
714        for func in self.func_dirs.keys() {
715            func.get().dir().set_bound(false);
716        }
717
718        remove_at(&self.dir)?;
719
720        for func in self.func_dirs.keys() {
721            func.get().dir().reset_dir();
722        }
723
724        for (func, dir) in &self.func_dirs {
725            func.get().post_removal(dir)?;
726        }
727
728        self.detach();
729        Ok(())
730    }
731
732    /// Unbind from the UDC and remove the USB gadget.
733    pub fn remove(mut self) -> Result<()> {
734        self.do_remove()
735    }
736}
737
738impl Drop for RegGadget {
739    fn drop(&mut self) {
740        if self.attached {
741            if let Err(err) = self.do_remove() {
742                log::warn!("removing gadget at {} failed: {err}", self.dir.display());
743            }
744        }
745    }
746}
747
748/// Remove USB gadget at specified configfs gadget directory.
749fn remove_at(dir: &Path) -> Result<()> {
750    log::debug!("removing gadget at {}", dir.display());
751
752    init_remove_handlers();
753
754    let _ = fs::write(dir.join("UDC"), "\n");
755
756    if let Ok(entries) = fs::read_dir(dir.join("os_desc")) {
757        for file in entries {
758            let Ok(file) = file else { continue };
759            let Ok(file_type) = file.file_type() else { continue };
760            if file_type.is_symlink() {
761                fs::remove_file(file.path())?;
762            }
763        }
764    }
765
766    for config_dir in fs::read_dir(dir.join("configs"))? {
767        let Ok(config_dir) = config_dir else { continue };
768        if !config_dir.metadata()?.is_dir() {
769            continue;
770        }
771
772        for func in fs::read_dir(config_dir.path())? {
773            let Ok(func) = func else { continue };
774            if func.metadata()?.is_symlink() {
775                fs::remove_file(func.path())?;
776            }
777        }
778
779        for lang in fs::read_dir(config_dir.path().join("strings"))? {
780            let Ok(lang) = lang else { continue };
781            if lang.metadata()?.is_dir() {
782                fs::remove_dir(lang.path())?;
783            }
784        }
785
786        fs::remove_dir(config_dir.path())?;
787    }
788
789    for func_dir in fs::read_dir(dir.join("functions"))? {
790        let Ok(func_dir) = func_dir else { continue };
791        if !func_dir.metadata()?.is_dir() {
792            continue;
793        }
794
795        call_remove_handler(&func_dir.path())?;
796
797        fs::remove_dir(func_dir.path())?;
798    }
799
800    for lang in fs::read_dir(dir.join("strings"))? {
801        let Ok(lang) = lang else { continue };
802        if lang.metadata()?.is_dir() {
803            fs::remove_dir(lang.path())?;
804        }
805    }
806
807    fs::remove_dir(dir)?;
808
809    log::debug!("removed gadget at {}", dir.display());
810    Ok(())
811}
812
813/// The path to the USB gadget configuration directory within configfs.
814fn usb_gadget_dir() -> Result<PathBuf> {
815    let _ = request_module("libcomposite");
816
817    let usb_gadget_dir = configfs_dir()?.join("usb_gadget");
818    if usb_gadget_dir.is_dir() {
819        Ok(usb_gadget_dir)
820    } else {
821        Err(Error::new(ErrorKind::NotFound, "usb_gadget not found in configfs"))
822    }
823}
824
825/// Get all USB gadgets registered on the system.
826///
827/// This returns all USB gadgets, including gadgets not created by the running program or
828/// registered by other means than using this library.
829pub fn registered() -> Result<Vec<RegGadget>> {
830    let usb_gadget_dir = usb_gadget_dir()?;
831
832    let mut gadgets = Vec::new();
833    for gadget_dir in fs::read_dir(usb_gadget_dir)? {
834        let Ok(gadget_dir) = gadget_dir else { continue };
835        if gadget_dir.metadata()?.is_dir() {
836            gadgets.push(RegGadget { dir: gadget_dir.path(), attached: false, func_dirs: HashMap::new() });
837        }
838    }
839
840    Ok(gadgets)
841}
842
843/// Remove all USB gadgets defined on the system.
844///
845/// This removes all USB gadgets, including gadgets not created by the running program or
846/// registered by other means than using this library.
847pub fn remove_all() -> Result<()> {
848    let mut res = Ok(());
849
850    for gadget in registered()? {
851        if let Err(err) = gadget.remove() {
852            res = Err(err);
853        }
854    }
855
856    res
857}
858
859/// Unbind all USB gadgets defined on the system.
860///
861/// This unbinds all USB gadgets, including gadgets not created by the running program or
862/// registered by other means than using this library.
863pub fn unbind_all() -> Result<()> {
864    let mut res = Ok(());
865
866    for gadget in registered()? {
867        if let Err(err) = gadget.bind(None) {
868            res = Err(err);
869        }
870    }
871
872    res
873}