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    write_attr, 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    ///
192    /// Should not be empty. The Linux kernel (as of version 7.3) always announces a serial
193    /// number string to the host; if empty, an empty string descriptor is provided instead of
194    /// omitting the serial number.
195    pub serial_number: String,
196}
197
198impl Strings {
199    /// Creates new USB device strings.
200    ///
201    /// See [`serial_number`](Self::serial_number) regarding empty serial numbers.
202    pub fn new(manufacturer: impl AsRef<str>, product: impl AsRef<str>, serial_number: impl AsRef<str>) -> Self {
203        Self {
204            manufacturer: manufacturer.as_ref().to_string(),
205            product: product.as_ref().to_string(),
206            serial_number: serial_number.as_ref().to_string(),
207        }
208    }
209}
210
211/// USB gadget operating system descriptor.
212#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
213pub struct OsDescriptor {
214    /// Vendor code for requests.
215    pub vendor_code: u8,
216    /// Signature.
217    pub qw_sign: String,
218    /// Index of configuration in [`Gadget::configs`] to be reported at index 0.
219    ///
220    /// Hosts which expect the "OS Descriptors" ask only for configurations at index 0,
221    /// but Linux-based USB devices can provide more than one configuration.
222    pub config: usize,
223}
224
225impl OsDescriptor {
226    /// Creates a new instance.
227    pub const fn new(vendor_code: u8, qw_sign: String) -> Self {
228        Self { vendor_code, qw_sign, config: 0 }
229    }
230
231    /// The Microsoft OS descriptor.
232    ///
233    /// Uses vendor code 0xf0 for requests.
234    pub fn microsoft() -> Self {
235        Self { vendor_code: 0xf0, qw_sign: "MSFT100".to_string(), config: 0 }
236    }
237}
238
239/// WebUSB version.
240#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
241pub enum WebUsbVersion {
242    /// Version 1.0
243    #[default]
244    V10,
245    /// Other version in BCD format.
246    Other(u16),
247}
248
249impl From<WebUsbVersion> for u16 {
250    fn from(value: WebUsbVersion) -> Self {
251        match value {
252            WebUsbVersion::V10 => 0x0100,
253            WebUsbVersion::Other(ver) => ver,
254        }
255    }
256}
257
258/// USB gadget WebUSB descriptor.
259#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
260pub struct WebUsb {
261    /// WebUSB specification version number.
262    pub version: WebUsbVersion,
263    /// bRequest value used for issuing WebUSB requests.
264    pub vendor_code: u8,
265    /// URL of the device's landing page.
266    pub landing_page: String,
267}
268
269impl WebUsb {
270    /// Creates a new instance.
271    pub fn new(vendor_code: u8, landing_page: impl AsRef<str>) -> Self {
272        Self { version: WebUsbVersion::default(), vendor_code, landing_page: landing_page.as_ref().to_string() }
273    }
274}
275
276/// USB gadget configuration.
277#[derive(Debug, Clone)]
278#[non_exhaustive]
279pub struct Config {
280    /// Maximum power in mA.
281    pub max_power: u16,
282    /// Self powered?
283    pub self_powered: bool,
284    /// Remote wakeup?
285    pub remote_wakeup: bool,
286    /// Configuration description string.
287    pub description: HashMap<Language, String>,
288    /// Functions, i.e. USB interfaces, present in this configuration.
289    pub functions: Vec<function::Handle>,
290}
291
292impl Config {
293    /// Creates a new USB gadget configuration.
294    pub fn new(description: impl AsRef<str>) -> Self {
295        Self {
296            max_power: 500,
297            self_powered: false,
298            remote_wakeup: false,
299            description: [(Language::default(), description.as_ref().to_string())].into(),
300            functions: Default::default(),
301        }
302    }
303
304    /// Adds a USB function (interface) to this configuration.
305    pub fn add_function(&mut self, function_handle: function::Handle) {
306        self.functions.push(function_handle);
307    }
308
309    /// Adds a USB function (interface) to this configuration.
310    #[must_use]
311    pub fn with_function(mut self, function_handle: function::Handle) -> Self {
312        self.add_function(function_handle);
313        self
314    }
315
316    fn register(
317        &self, gadget_dir: &Path, idx: usize, func_dirs: &HashMap<function::Handle, PathBuf>,
318    ) -> Result<PathBuf> {
319        let dir = gadget_dir.join("configs").join(format!("c.{idx}"));
320        log::debug!("creating config at {}", dir.display());
321        fs::create_dir(&dir)?;
322
323        let mut attributes = 1 << 7;
324        if self.self_powered {
325            attributes |= 1 << 6;
326        }
327        if self.remote_wakeup {
328            attributes |= 1 << 5;
329        }
330
331        write_attr(&dir.join("bmAttributes"), hex_u8(attributes))?;
332        write_attr(&dir.join("MaxPower"), self.max_power.to_string())?;
333
334        for (&lang, desc) in &self.description {
335            let lang_dir = dir.join("strings").join(hex_u16(lang.into()));
336            fs::create_dir(&lang_dir)?;
337            write_attr(&lang_dir.join("configuration"), desc)?;
338        }
339
340        for func in &self.functions {
341            let func_dir = &func_dirs[func];
342            log::debug!("adding function {}", func_dir.display());
343            symlink(func_dir, dir.join(func_dir.file_name().unwrap()))?;
344        }
345
346        Ok(dir)
347    }
348}
349
350/// USB version.
351#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
352pub enum UsbVersion {
353    /// USB 1.1
354    V11,
355    /// USB 2.0
356    #[default]
357    V20,
358    /// USB 2.0 with BOS descriptor support (bcdUSB 0x0201), needed for WinUSB.
359    V21,
360    /// USB 3.0
361    V30,
362    /// USB 3.1
363    V31,
364    /// Other version in BCD format.
365    Other(u16),
366}
367
368impl From<UsbVersion> for u16 {
369    fn from(value: UsbVersion) -> Self {
370        match value {
371            UsbVersion::V11 => 0x0110,
372            UsbVersion::V20 => 0x0200,
373            UsbVersion::V21 => 0x0201,
374            UsbVersion::V30 => 0x0300,
375            UsbVersion::V31 => 0x0310,
376            UsbVersion::Other(ver) => ver,
377        }
378    }
379}
380
381/// USB gadget definition.
382///
383/// Fields set to `None` are left at their kernel-provided default values.
384#[derive(Debug, Clone)]
385#[non_exhaustive]
386pub struct Gadget {
387    /// Custom name for the gadget in configfs.
388    ///
389    /// If `None`, an auto-generated name like `usb-gadget0` is used.
390    pub name: Option<String>,
391    /// USB device class.
392    pub device_class: Class,
393    /// USB device id.
394    pub id: Id,
395    /// USB device strings.
396    pub strings: HashMap<Language, Strings>,
397    /// Maximum endpoint 0 packet size.
398    pub max_packet_size0: u8,
399    /// Device release number in BCD format.
400    ///
401    /// No hexadecimal digit must exceed 9.
402    pub device_release: u16,
403    /// USB specification version.
404    pub usb_version: UsbVersion,
405    /// Maximum speed supported by driver.
406    pub max_speed: Option<Speed>,
407    /// OS descriptor extension.
408    ///
409    /// Skipped with a warning if unsupported by the kernel.
410    pub os_descriptor: Option<OsDescriptor>,
411    /// WebUSB extension.
412    ///
413    /// Skipped with a warning if unsupported by the kernel.
414    pub web_usb: Option<WebUsb>,
415    /// USB device configurations.
416    pub configs: Vec<Config>,
417    /// Keep the partially created gadget in configfs if registration or binding fails.
418    ///
419    /// By default, a gadget that fails to register or bind is removed.
420    /// Enable this to inspect the partial configuration for debugging;
421    /// it then has to be removed manually, e.g. using [`remove_all`].
422    pub keep_on_error: bool,
423}
424
425impl Gadget {
426    /// Creates a new USB gadget definition.
427    pub fn new(device_class: Class, id: Id, strings: Strings) -> Self {
428        Self {
429            name: None,
430            device_class,
431            id,
432            strings: [(Language::default(), strings)].into(),
433            max_packet_size0: 64,
434            device_release: 0x0000,
435            usb_version: UsbVersion::default(),
436            max_speed: None,
437            os_descriptor: None,
438            web_usb: None,
439            configs: Vec::new(),
440            keep_on_error: false,
441        }
442    }
443
444    /// Adds a USB device configuration.
445    pub fn add_config(&mut self, config: Config) {
446        self.configs.push(config);
447    }
448
449    /// Adds a USB device configuration.
450    #[must_use]
451    pub fn with_config(mut self, config: Config) -> Self {
452        self.add_config(config);
453        self
454    }
455
456    /// Sets the OS descriptor.
457    #[must_use]
458    pub fn with_os_descriptor(mut self, os_descriptor: OsDescriptor) -> Self {
459        self.os_descriptor = Some(os_descriptor);
460        self
461    }
462
463    /// Sets the WebUSB extension.
464    #[must_use]
465    pub fn with_web_usb(mut self, web_usb: WebUsb) -> Self {
466        self.web_usb = Some(web_usb);
467        self
468    }
469
470    /// Register the USB gadget.
471    ///
472    /// At least one [configuration](Config) must be added before the gadget
473    /// can be registered.
474    ///
475    /// If registration fails, the partially created gadget is removed,
476    /// unless [`keep_on_error`](Self::keep_on_error) is set.
477    #[must_use = "consumes the gadget"]
478    pub fn register(self) -> Result<RegGadget> {
479        if self.configs.is_empty() {
480            return Err(Error::new(ErrorKind::InvalidInput, "USB gadget must have at least one configuration"));
481        }
482
483        let usb_gadget_dir = usb_gadget_dir()?;
484
485        let dir = if let Some(ref name) = self.name {
486            let dir = usb_gadget_dir.join(name);
487            fs::create_dir(&dir)?;
488            dir
489        } else {
490            let mut gadget_idx: u16 = 0;
491            loop {
492                let dir = usb_gadget_dir.join(format!("usb-gadget{gadget_idx}"));
493                match fs::create_dir(&dir) {
494                    Ok(()) => break dir,
495                    Err(err) if err.kind() == ErrorKind::AlreadyExists => (),
496                    Err(err) => return Err(err),
497                }
498                gadget_idx = gadget_idx
499                    .checked_add(1)
500                    .ok_or_else(|| Error::new(ErrorKind::OutOfMemory, "USB gadgets exhausted"))?;
501            }
502        };
503
504        log::debug!("registering gadget at {}", dir.display());
505
506        // The gadget is removed when `reg` is dropped on failure, unless kept for debugging.
507        let mut reg = RegGadget { dir, attached: true, func_dirs: HashMap::new() };
508        if let Err(err) = self.populate(&mut reg) {
509            if self.keep_on_error {
510                log::warn!("keeping partially registered gadget at {} for debugging", reg.dir.display());
511                reg.detach();
512            }
513            return Err(err);
514        }
515
516        log::debug!("gadget at {} registered", reg.dir.display());
517        Ok(reg)
518    }
519
520    /// Writes the gadget configuration into its configfs directory.
521    fn populate(&self, reg: &mut RegGadget) -> Result<()> {
522        let dir = reg.dir.clone();
523
524        write_attr(&dir.join("bDeviceClass"), hex_u8(self.device_class.class))?;
525        write_attr(&dir.join("bDeviceSubClass"), hex_u8(self.device_class.sub_class))?;
526        write_attr(&dir.join("bDeviceProtocol"), hex_u8(self.device_class.protocol))?;
527
528        write_attr(&dir.join("idVendor"), hex_u16(self.id.vendor))?;
529        write_attr(&dir.join("idProduct"), hex_u16(self.id.product))?;
530
531        write_attr(&dir.join("bMaxPacketSize0"), hex_u8(self.max_packet_size0))?;
532        write_attr(&dir.join("bcdDevice"), hex_u16(self.device_release))?;
533        write_attr(&dir.join("bcdUSB"), hex_u16(self.usb_version.into()))?;
534
535        if let Some(v) = self.max_speed {
536            write_attr(&dir.join("max_speed"), v.to_string())?;
537        }
538
539        if let Some(webusb) = &self.web_usb {
540            let webusb_dir = dir.join("webusb");
541            if webusb_dir.is_dir() {
542                write_attr(&webusb_dir.join("bVendorCode"), hex_u8(webusb.vendor_code))?;
543                write_attr(&webusb_dir.join("bcdVersion"), hex_u16(webusb.version.into()))?;
544                write_attr(&webusb_dir.join("landingPage"), &webusb.landing_page)?;
545                write_attr(&webusb_dir.join("use"), "1")?;
546            } else {
547                log::warn!("WebUSB descriptor is unsupported by kernel");
548            }
549        }
550
551        for (&lang, strs) in &self.strings {
552            let lang_dir = dir.join("strings").join(hex_u16(lang.into()));
553            fs::create_dir(&lang_dir)?;
554
555            write_attr(&lang_dir.join("manufacturer"), &strs.manufacturer)?;
556            write_attr(&lang_dir.join("product"), &strs.product)?;
557            write_attr(&lang_dir.join("serialnumber"), &strs.serial_number)?;
558        }
559
560        let gadget_name = dir.file_name().unwrap().to_string_lossy();
561        let mut functions = Vec::new();
562        for func in self.configs.iter().flat_map(|c| &c.functions) {
563            if !functions.contains(&func) {
564                functions.push(func);
565            }
566        }
567        for (func_idx, &func) in functions.iter().enumerate() {
568            let func_dir = dir.join(
569                dir.join("functions")
570                    .join(format!("{}.{gadget_name}-{func_idx}", func.get().driver().to_string_lossy())),
571            );
572            log::debug!("creating function at {}", func_dir.display());
573            fs::create_dir(&func_dir)?;
574
575            // Track the function before registering it, so that it is cleaned up on failure.
576            func.get().dir().set_dir(&func_dir);
577            reg.func_dirs.insert(func.clone(), func_dir);
578            func.get().register()?;
579        }
580
581        let mut config_dirs = Vec::new();
582        for (idx, config) in self.configs.iter().enumerate() {
583            let dir = config.register(&dir, idx + 1, &reg.func_dirs)?;
584            config_dirs.push(dir);
585        }
586
587        if let Some(os_desc) = &self.os_descriptor {
588            let os_desc_dir = dir.join("os_desc");
589            if os_desc_dir.is_dir() {
590                write_attr(&os_desc_dir.join("b_vendor_code"), hex_u8(os_desc.vendor_code))?;
591                write_attr(&os_desc_dir.join("qw_sign"), &os_desc.qw_sign)?;
592                write_attr(&os_desc_dir.join("use"), "1")?;
593
594                let config_dir = config_dirs.get(os_desc.config).ok_or_else(|| {
595                    Error::new(ErrorKind::InvalidInput, "invalid configuration index in OS descriptor")
596                })?;
597                log::debug!("linking OS descriptor to config dir {}", config_dir.display());
598                symlink(config_dir, os_desc_dir.join(config_dir.file_name().unwrap()))?;
599            } else {
600                log::warn!("USB OS descriptor is unsupported by kernel");
601            }
602        }
603
604        Ok(())
605    }
606
607    /// Register and bind USB gadget to a USB device controller (UDC).
608    ///
609    /// At least one [configuration](Config) must be added before the gadget
610    /// can be bound.
611    ///
612    /// If registration or binding fails, the gadget is removed,
613    /// unless [`keep_on_error`](Self::keep_on_error) is set.
614    #[must_use = "consumes the gadget"]
615    pub fn bind(self, udc: &Udc) -> Result<RegGadget> {
616        let keep_on_error = self.keep_on_error;
617        let mut reg = self.register()?;
618        if let Err(err) = reg.bind(Some(udc)) {
619            if keep_on_error {
620                log::warn!("keeping gadget at {} that failed to bind for debugging", reg.dir.display());
621                reg.detach();
622            }
623            return Err(err);
624        }
625        Ok(reg)
626    }
627}
628
629/// USB gadget registered with the system.
630///
631/// A function instance registered in a USB gadget.
632#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
633pub struct RegFunction {
634    /// Function driver name, e.g. "acm", "ecm", "mass_storage".
635    driver: String,
636    /// Instance name, e.g. "composite-0".
637    instance: String,
638}
639
640impl RegFunction {
641    /// Function driver name, e.g. "acm", "ecm", "mass_storage".
642    pub fn driver(&self) -> &str {
643        &self.driver
644    }
645
646    /// Instance name, e.g. "composite-0".
647    pub fn instance(&self) -> &str {
648        &self.instance
649    }
650}
651
652impl fmt::Display for RegFunction {
653    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
654        write!(f, "{}.{}", self.driver, self.instance)
655    }
656}
657
658/// If this was obtained by calling [`Gadget::bind`], the USB gadget will be
659/// unbound and removed when this is dropped.
660///
661/// Call [`registered`] to obtain all gadgets registered on the system.
662#[must_use = "The USB gadget is removed when RegGadget is dropped."]
663pub struct RegGadget {
664    dir: PathBuf,
665    attached: bool,
666    func_dirs: HashMap<Handle, PathBuf>,
667}
668
669impl fmt::Debug for RegGadget {
670    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
671        f.debug_struct("RegGadget").field("name", &self.name()).field("is_attached", &self.is_attached()).finish()
672    }
673}
674
675impl RegGadget {
676    /// Name of this USB gadget in configfs.
677    pub fn name(&self) -> &OsStr {
678        self.dir.file_name().unwrap()
679    }
680
681    /// Path of this USB gadget in configfs.
682    pub fn path(&self) -> &Path {
683        &self.dir
684    }
685
686    /// If true, the USB gadget will be removed when this is dropped.
687    pub fn is_attached(&self) -> bool {
688        self.attached
689    }
690
691    /// The name of the USB device controller (UDC) this gadget is bound to.
692    pub fn udc(&self) -> Result<Option<OsString>> {
693        let data = OsString::from_vec(fs::read(self.dir.join("UDC"))?);
694        let data = trim_os_str(&data);
695        if data.is_empty() {
696            Ok(None)
697        } else {
698            Ok(Some(data.to_os_string()))
699        }
700    }
701
702    /// Returns all function instances registered in this gadget.
703    pub fn functions(&self) -> Result<Vec<RegFunction>> {
704        let func_dir = self.dir.join("functions");
705        let mut functions = Vec::new();
706
707        if let Ok(entries) = fs::read_dir(&func_dir) {
708            for entry in entries {
709                let Ok(entry) = entry else { continue };
710                if !entry.metadata()?.is_dir() {
711                    continue;
712                }
713                let name = entry.file_name().to_string_lossy().to_string();
714                if let Some((driver, instance)) = name.split_once('.') {
715                    functions.push(RegFunction { driver: driver.to_string(), instance: instance.to_string() });
716                }
717            }
718        }
719
720        functions.sort();
721        Ok(functions)
722    }
723
724    /// Binds the gadget to the specified USB device controller (UDC).
725    ///
726    /// If `udc` is `None`, the gadget is unbound from any UDC.
727    pub fn bind(&self, udc: Option<&Udc>) -> Result<()> {
728        log::debug!("binding gadget {:?} to {:?}", self, udc);
729
730        let name = match udc {
731            Some(udc) => udc.name().to_os_string(),
732            None => "\n".into(),
733        };
734
735        match fs::write(self.dir.join("UDC"), name.as_bytes()) {
736            Ok(()) => (),
737            Err(err) if udc.is_none() && err.raw_os_error() == Some(Errno::NODEV.raw_os_error()) => (),
738            Err(err) => return Err(err),
739        }
740
741        for func in self.func_dirs.keys() {
742            func.get().dir().set_bound(udc.is_some());
743        }
744
745        Ok(())
746    }
747
748    /// Detach the handle from the USB gadget while keeping the USB gadget active.
749    ///
750    /// This prevents the USB gadget from being deregistered when this handle is dropped.
751    pub fn detach(&mut self) {
752        self.attached = false;
753    }
754
755    fn do_remove(&mut self) -> Result<()> {
756        for func in self.func_dirs.keys() {
757            func.get().pre_removal()?;
758        }
759
760        for func in self.func_dirs.keys() {
761            func.get().dir().set_bound(false);
762        }
763
764        remove_at(&self.dir)?;
765
766        for func in self.func_dirs.keys() {
767            func.get().dir().reset_dir();
768        }
769
770        for (func, dir) in &self.func_dirs {
771            func.get().post_removal(dir)?;
772        }
773
774        self.detach();
775        Ok(())
776    }
777
778    /// Unbind from the UDC and remove the USB gadget.
779    pub fn remove(mut self) -> Result<()> {
780        self.do_remove()
781    }
782}
783
784impl Drop for RegGadget {
785    fn drop(&mut self) {
786        if self.attached {
787            if let Err(err) = self.do_remove() {
788                log::warn!("removing gadget at {} failed: {err}", self.dir.display());
789            }
790        }
791    }
792}
793
794/// Remove USB gadget at specified configfs gadget directory.
795fn remove_at(dir: &Path) -> Result<()> {
796    log::debug!("removing gadget at {}", dir.display());
797
798    init_remove_handlers();
799
800    let _ = fs::write(dir.join("UDC"), "\n");
801
802    if let Ok(entries) = fs::read_dir(dir.join("os_desc")) {
803        for file in entries {
804            let Ok(file) = file else { continue };
805            let Ok(file_type) = file.file_type() else { continue };
806            if file_type.is_symlink() {
807                fs::remove_file(file.path())?;
808            }
809        }
810    }
811
812    for config_dir in fs::read_dir(dir.join("configs"))? {
813        let Ok(config_dir) = config_dir else { continue };
814        if !config_dir.metadata()?.is_dir() {
815            continue;
816        }
817
818        for func in fs::read_dir(config_dir.path())? {
819            let Ok(func) = func else { continue };
820            if func.metadata()?.is_symlink() {
821                fs::remove_file(func.path())?;
822            }
823        }
824
825        for lang in fs::read_dir(config_dir.path().join("strings"))? {
826            let Ok(lang) = lang else { continue };
827            if lang.metadata()?.is_dir() {
828                fs::remove_dir(lang.path())?;
829            }
830        }
831
832        fs::remove_dir(config_dir.path())?;
833    }
834
835    for func_dir in fs::read_dir(dir.join("functions"))? {
836        let Ok(func_dir) = func_dir else { continue };
837        if !func_dir.metadata()?.is_dir() {
838            continue;
839        }
840
841        call_remove_handler(&func_dir.path())?;
842
843        fs::remove_dir(func_dir.path())?;
844    }
845
846    for lang in fs::read_dir(dir.join("strings"))? {
847        let Ok(lang) = lang else { continue };
848        if lang.metadata()?.is_dir() {
849            fs::remove_dir(lang.path())?;
850        }
851    }
852
853    fs::remove_dir(dir)?;
854
855    log::debug!("removed gadget at {}", dir.display());
856    Ok(())
857}
858
859/// The path to the USB gadget configuration directory within configfs.
860fn usb_gadget_dir() -> Result<PathBuf> {
861    let _ = request_module("libcomposite");
862
863    let usb_gadget_dir = configfs_dir()?.join("usb_gadget");
864    if usb_gadget_dir.is_dir() {
865        Ok(usb_gadget_dir)
866    } else {
867        Err(Error::new(ErrorKind::NotFound, "usb_gadget not found in configfs"))
868    }
869}
870
871/// Get all USB gadgets registered on the system.
872///
873/// This returns all USB gadgets, including gadgets not created by the running program or
874/// registered by other means than using this library.
875pub fn registered() -> Result<Vec<RegGadget>> {
876    let usb_gadget_dir = usb_gadget_dir()?;
877
878    let mut gadgets = Vec::new();
879    for gadget_dir in fs::read_dir(usb_gadget_dir)? {
880        let Ok(gadget_dir) = gadget_dir else { continue };
881        if gadget_dir.metadata()?.is_dir() {
882            gadgets.push(RegGadget { dir: gadget_dir.path(), attached: false, func_dirs: HashMap::new() });
883        }
884    }
885
886    Ok(gadgets)
887}
888
889/// Remove all USB gadgets defined on the system.
890///
891/// This removes all USB gadgets, including gadgets not created by the running program or
892/// registered by other means than using this library.
893pub fn remove_all() -> Result<()> {
894    let mut res = Ok(());
895
896    for gadget in registered()? {
897        if let Err(err) = gadget.remove() {
898            res = Err(err);
899        }
900    }
901
902    res
903}
904
905/// Unbind all USB gadgets defined on the system.
906///
907/// This unbinds all USB gadgets, including gadgets not created by the running program or
908/// registered by other means than using this library.
909pub fn unbind_all() -> Result<()> {
910    let mut res = Ok(());
911
912    for gadget in registered()? {
913        if let Err(err) = gadget.bind(None) {
914            res = Err(err);
915        }
916    }
917
918    res
919}