Skip to main content

usb_gadget/function/custom/
ffs.rs

1//! FunctionFS bindings.
2
3use bitflags::bitflags;
4use byteorder::{ReadBytesExt, WriteBytesExt, LE};
5use rustix::{
6    ioctl::{self, opcode},
7    mount::{MountFlags, UnmountFlags},
8};
9use std::{
10    collections::HashMap,
11    ffi::{c_int, CStr, CString, OsStr},
12    fmt,
13    io::{ErrorKind, Read, Write},
14    num::TryFromIntError,
15    os::fd::{BorrowedFd, RawFd},
16    path::Path,
17};
18
19use crate::{
20    ioctl::{IntReturnInt, ReturnInt},
21    linux_version, Language,
22};
23
24#[derive(Debug, Clone)]
25pub enum Error {
26    StringsDifferAcrossLanguages,
27    Overflow,
28}
29
30impl fmt::Display for Error {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        match self {
33            Error::StringsDifferAcrossLanguages => write!(f, "string count differs across languages"),
34            Error::Overflow => write!(f, "too many descriptor entries"),
35        }
36    }
37}
38
39impl std::error::Error for Error {}
40
41impl From<std::io::Error> for Error {
42    fn from(_: std::io::Error) -> Self {
43        unreachable!()
44    }
45}
46
47impl From<TryFromIntError> for Error {
48    fn from(_: TryFromIntError) -> Self {
49        Self::Overflow
50    }
51}
52
53impl From<Error> for std::io::Error {
54    fn from(err: Error) -> Self {
55        std::io::Error::new(ErrorKind::InvalidInput, err)
56    }
57}
58
59type Result<T> = std::result::Result<T, Error>;
60
61/// USB direction to device.
62pub const DIR_OUT: u8 = 0x00;
63/// USB direction to host.
64pub const DIR_IN: u8 = 0x80;
65
66/// `EL2HLT` errno returned by FunctionFS when stalling endpoint 0.
67pub const EL2HLT: i32 = 51;
68
69bitflags! {
70    #[derive(Clone, Copy, Debug)]
71    pub struct Flags: u32 {
72        const HAS_FS_DESC = 1;
73        const HAS_HS_DESC = 2;
74        const HAS_SS_DESC = 4;
75        const HAS_MS_OS_DESC = 8;
76        const VIRTUAL_ADDR = 16;
77        const EVENTFD = 32;
78        const ALL_CTRL_RECIP = 64;
79        const CONFIG0_SETUP = 128;
80    }
81}
82
83#[derive(Clone, Debug)]
84pub struct Descs {
85    pub flags: Flags,
86    pub eventfd: Option<RawFd>,
87    pub fs_descrs: Vec<Desc>,
88    pub hs_descrs: Vec<Desc>,
89    pub ss_descrs: Vec<Desc>,
90    pub os_descrs: Vec<OsDesc>,
91}
92
93impl Descs {
94    const MAGIC_V2: u32 = 3;
95
96    pub fn to_bytes(&self) -> Result<Vec<u8>> {
97        let mut data = Vec::new();
98
99        data.write_u32::<LE>(Self::MAGIC_V2)?;
100        data.write_u32::<LE>(0)?; // length
101
102        let mut flags = self.flags;
103        flags.insert(Flags::HAS_FS_DESC | Flags::HAS_HS_DESC | Flags::HAS_SS_DESC | Flags::HAS_MS_OS_DESC);
104        flags.set(Flags::EVENTFD, self.eventfd.is_some());
105        data.write_u32::<LE>(flags.bits())?;
106
107        if let Some(fd) = self.eventfd {
108            data.write_i32::<LE>(fd)?;
109        }
110
111        data.write_u32::<LE>(self.fs_descrs.len().try_into()?)?;
112        data.write_u32::<LE>(self.hs_descrs.len().try_into()?)?;
113        data.write_u32::<LE>(self.ss_descrs.len().try_into()?)?;
114        data.write_u32::<LE>(self.os_descrs.len().try_into()?)?;
115
116        for fs_descr in &self.fs_descrs {
117            data.extend(fs_descr.to_bytes()?);
118        }
119        for hs_descr in &self.hs_descrs {
120            data.extend(hs_descr.to_bytes()?);
121        }
122        for ss_descr in &self.ss_descrs {
123            data.extend(ss_descr.to_bytes()?);
124        }
125        for os_descr in &self.os_descrs {
126            data.extend(os_descr.to_bytes()?);
127        }
128
129        let len: u32 = data.len().try_into()?;
130        data[4..8].copy_from_slice(&len.to_le_bytes());
131
132        Ok(data)
133    }
134}
135
136#[derive(Clone, Debug)]
137pub enum Desc {
138    Interface(InterfaceDesc),
139    Endpoint(EndpointDesc),
140    SsEndpointComp(SsEndpointComp),
141    InterfaceAssoc(InterfaceAssocDesc),
142    Custom(CustomDesc),
143}
144
145impl From<InterfaceDesc> for Desc {
146    fn from(value: InterfaceDesc) -> Self {
147        Self::Interface(value)
148    }
149}
150
151impl From<EndpointDesc> for Desc {
152    fn from(value: EndpointDesc) -> Self {
153        Self::Endpoint(value)
154    }
155}
156
157impl From<SsEndpointComp> for Desc {
158    fn from(value: SsEndpointComp) -> Self {
159        Self::SsEndpointComp(value)
160    }
161}
162
163impl From<InterfaceAssocDesc> for Desc {
164    fn from(value: InterfaceAssocDesc) -> Self {
165        Self::InterfaceAssoc(value)
166    }
167}
168
169impl From<CustomDesc> for Desc {
170    fn from(value: CustomDesc) -> Self {
171        Self::Custom(value)
172    }
173}
174
175impl Desc {
176    fn to_bytes(&self) -> Result<Vec<u8>> {
177        let mut data = Vec::new();
178
179        data.write_u8(0)?;
180
181        match self {
182            Self::Interface(d) => d.write(&mut data)?,
183            Self::Endpoint(d) => d.write(&mut data)?,
184            Self::SsEndpointComp(d) => d.write(&mut data)?,
185            Self::InterfaceAssoc(d) => d.write(&mut data)?,
186            Self::Custom(d) => d.write(&mut data)?,
187        }
188
189        data[0] = data.len().try_into()?;
190        Ok(data)
191    }
192}
193
194#[derive(Clone, Debug)]
195pub struct InterfaceDesc {
196    pub interface_number: u8,
197    pub alternate_setting: u8,
198    pub num_endpoints: u8,
199    pub interface_class: u8,
200    pub interface_sub_class: u8,
201    pub interface_protocol: u8,
202    pub name_idx: u8,
203}
204
205impl InterfaceDesc {
206    pub const TYPE: u8 = 0x04;
207
208    fn write(&self, data: &mut Vec<u8>) -> Result<()> {
209        data.write_u8(Self::TYPE)?;
210        data.write_u8(self.interface_number)?;
211        data.write_u8(self.alternate_setting)?;
212        data.write_u8(self.num_endpoints)?;
213        data.write_u8(self.interface_class)?;
214        data.write_u8(self.interface_sub_class)?;
215        data.write_u8(self.interface_protocol)?;
216        data.write_u8(self.name_idx)?;
217        Ok(())
218    }
219}
220
221/// USB endpoint descriptor.
222#[derive(Clone, Debug)]
223pub struct EndpointDesc {
224    /// Endpoint address.
225    pub endpoint_address: u8,
226    /// Attributes.
227    pub attributes: u8,
228    /// Maximum packet size.
229    pub max_packet_size: u16,
230    /// Interval.
231    pub interval: u8,
232    /// Audio-endpoint specific data.
233    pub audio: Option<AudioEndpointDesc>,
234}
235
236/// Audio-part of USB endpoint descriptor.
237#[derive(Clone, Debug)]
238pub struct AudioEndpointDesc {
239    /// Refresh.
240    pub refresh: u8,
241    /// Sync address.
242    pub synch_address: u8,
243}
244
245impl EndpointDesc {
246    /// Endpoint descriptor type.
247    pub const TYPE: u8 = 0x05;
248
249    /// Size without audio.
250    pub const SIZE: usize = 7;
251    /// Size with audio.
252    pub const AUDIO_SIZE: usize = 9;
253
254    fn write(&self, data: &mut Vec<u8>) -> Result<()> {
255        data.write_u8(Self::TYPE)?;
256        data.write_u8(self.endpoint_address)?;
257        data.write_u8(self.attributes)?;
258        data.write_u16::<LE>(self.max_packet_size)?;
259        data.write_u8(self.interval)?;
260
261        if let Some(audio) = &self.audio {
262            data.write_u8(audio.refresh)?;
263            data.write_u8(audio.synch_address)?;
264        }
265
266        Ok(())
267    }
268
269    /// Parse from raw descriptor data.
270    pub fn parse(mut data: &[u8]) -> std::io::Result<Self> {
271        let size = data.read_u8()?;
272        if usize::from(size) != Self::SIZE && usize::from(size) != Self::AUDIO_SIZE {
273            return Err(std::io::Error::new(
274                std::io::ErrorKind::InvalidData,
275                "endpoint descriptor size mismatch",
276            ));
277        }
278
279        if data.read_u8()? != Self::TYPE {
280            return Err(std::io::Error::new(
281                std::io::ErrorKind::InvalidData,
282                "endpoint descriptor type mismatch",
283            ));
284        }
285
286        let endpoint_address = data.read_u8()?;
287        let attributes = data.read_u8()?;
288        let max_packet_size = data.read_u16::<LE>()?;
289        let interval = data.read_u8()?;
290
291        let audio = if usize::from(size) == Self::AUDIO_SIZE {
292            let refresh = data.read_u8()?;
293            let synch_address = data.read_u8()?;
294            Some(AudioEndpointDesc { refresh, synch_address })
295        } else {
296            None
297        };
298
299        Ok(Self { endpoint_address, attributes, max_packet_size, interval, audio })
300    }
301}
302
303#[derive(Clone, Debug)]
304pub struct SsEndpointComp {
305    pub max_burst: u8,
306    pub attributes: u8,
307    pub bytes_per_interval: u16,
308}
309
310impl SsEndpointComp {
311    pub const TYPE: u8 = 0x30;
312
313    fn write(&self, data: &mut Vec<u8>) -> Result<()> {
314        data.write_u8(Self::TYPE)?;
315        data.write_u8(self.max_burst)?;
316        data.write_u8(self.attributes)?;
317        data.write_u16::<LE>(self.bytes_per_interval)?;
318        Ok(())
319    }
320}
321
322#[derive(Clone, Debug)]
323pub struct InterfaceAssocDesc {
324    pub first_interface: u8,
325    pub interface_count: u8,
326    pub function_class: u8,
327    pub function_sub_class: u8,
328    pub function_protocol: u8,
329    pub name_idx: u8,
330}
331
332impl InterfaceAssocDesc {
333    pub const TYPE: u8 = 0x0b;
334
335    fn write(&self, data: &mut Vec<u8>) -> Result<()> {
336        data.write_u8(Self::TYPE)?;
337        data.write_u8(self.first_interface)?;
338        data.write_u8(self.interface_count)?;
339        data.write_u8(self.function_class)?;
340        data.write_u8(self.function_sub_class)?;
341        data.write_u8(self.function_protocol)?;
342        data.write_u8(self.name_idx)?;
343        Ok(())
344    }
345}
346
347#[derive(Clone, Debug)]
348pub struct OsDesc {
349    pub interface: u8,
350    pub ext: OsDescExt,
351}
352
353impl OsDesc {
354    fn to_bytes(&self) -> Result<Vec<u8>> {
355        let mut data = Vec::new();
356        data.write_u8(self.interface)?;
357        data.write_u32::<LE>(0)?; // length
358
359        self.ext.write(&mut data)?;
360
361        let len: u32 = data.len().try_into()?;
362        data[1..5].copy_from_slice(&len.to_le_bytes());
363        Ok(data)
364    }
365}
366
367#[derive(Clone, Debug)]
368pub enum OsDescExt {
369    ExtCompat(Vec<OsExtCompat>),
370    ExtProp(Vec<OsExtProp>),
371}
372
373impl OsDescExt {
374    fn write(&self, data: &mut Vec<u8>) -> Result<()> {
375        let os_desc_bcd_version = if linux_version().unwrap_or_default() >= (6, 4) { 0x0100 } else { 0x0001 };
376
377        match self {
378            Self::ExtCompat(compats) => {
379                data.write_u16::<LE>(os_desc_bcd_version)?; // bcdVersion
380                data.write_u16::<LE>(4)?; // wIndex
381                data.write_u8(compats.len().try_into()?)?;
382                data.write_u8(0)?;
383
384                for compat in compats {
385                    compat.write(data)?;
386                }
387            }
388            Self::ExtProp(props) => {
389                data.write_u16::<LE>(os_desc_bcd_version)?; // bcdVersion
390                data.write_u16::<LE>(5)?; // wIndex
391                data.write_u16::<LE>(props.len().try_into()?)?;
392
393                for prop in props {
394                    data.extend(prop.to_bytes()?);
395                }
396            }
397        }
398        Ok(())
399    }
400}
401
402#[derive(Clone, Debug)]
403pub struct OsExtCompat {
404    pub first_interface_number: u8,
405    pub compatible_id: [u8; 8],
406    pub sub_compatible_id: [u8; 8],
407}
408
409impl OsExtCompat {
410    fn write(&self, data: &mut Vec<u8>) -> Result<()> {
411        data.write_u8(self.first_interface_number)?;
412        data.write_u8(1)?;
413        data.extend_from_slice(&self.compatible_id);
414        data.extend_from_slice(&self.sub_compatible_id);
415        data.extend_from_slice(&[0; 6]);
416        Ok(())
417    }
418}
419
420#[derive(Clone, Debug)]
421pub struct OsExtProp {
422    pub data_type: u32,
423    pub name: String,
424    pub data: Vec<u8>,
425}
426
427impl OsExtProp {
428    fn to_bytes(&self) -> Result<Vec<u8>> {
429        let mut data = Vec::new();
430
431        data.write_u32::<LE>(0)?; // length
432        data.write_u32::<LE>(self.data_type)?;
433        data.write_u16::<LE>(self.name.len().try_into()?)?;
434        data.write_all(self.name.as_bytes())?;
435        data.write_u32::<LE>(self.data.len().try_into()?)?;
436        data.write_all(&self.data)?;
437
438        let len: u32 = data.len().try_into()?;
439        assert_eq!(len as usize, 14 + self.name.len() + self.data.len(), "invalid OS descriptor length");
440        data[0..4].copy_from_slice(&len.to_le_bytes());
441        Ok(data)
442    }
443}
444
445/// Custom descriptor.
446///
447/// The raw descriptor published will be of the form:
448/// `[length, descriptor_type, data...]`
449#[derive(Clone, Debug)]
450pub struct CustomDesc {
451    /// Descriptor type.
452    pub descriptor_type: u8,
453    /// Custom data.
454    pub data: Vec<u8>,
455}
456
457impl CustomDesc {
458    /// Creates a new custom descriptor.
459    ///
460    /// The data must not include the length and descriptor type.
461    pub fn new(descriptor_type: u8, data: Vec<u8>) -> Self {
462        Self { descriptor_type, data }
463    }
464
465    fn write(&self, data: &mut Vec<u8>) -> Result<()> {
466        data.write_u8(self.descriptor_type)?;
467        data.write_all(&self.data)?;
468        Ok(())
469    }
470}
471
472#[derive(Clone, Debug)]
473pub struct Strings(pub HashMap<Language, Vec<String>>);
474
475impl Strings {
476    const MAGIC: u32 = 2;
477
478    pub fn to_bytes(&self) -> Result<Vec<u8>> {
479        let str_count = self.0.values().next().map(|v| v.len()).unwrap_or_default();
480        if !self.0.values().all(|v| v.len() == str_count) {
481            return Err(Error::StringsDifferAcrossLanguages);
482        }
483
484        let mut data = Vec::new();
485
486        data.write_u32::<LE>(Self::MAGIC)?;
487        data.write_u32::<LE>(0)?; // length
488        data.write_u32::<LE>(str_count.try_into()?)?;
489        data.write_u32::<LE>(self.0.len().try_into()?)?;
490
491        for (lang, strings) in &self.0 {
492            data.write_u16::<LE>((*lang).into())?;
493            for str in strings {
494                data.write_all(str.as_bytes())?;
495                data.write_u8(0)?;
496            }
497        }
498
499        let len: u32 = data.len().try_into()?;
500        data[4..8].copy_from_slice(&len.to_le_bytes());
501        Ok(data)
502    }
503}
504
505/// USB control request.
506#[derive(Clone, Debug)]
507pub struct CtrlReq {
508    /// Request type.
509    pub request_type: u8,
510    /// Request.
511    pub request: u8,
512    /// Value.
513    pub value: u16,
514    /// Index.
515    pub index: u16,
516    /// Length.
517    pub length: u16,
518}
519
520impl CtrlReq {
521    /// Parse from buffer.
522    pub fn parse(mut buf: &[u8]) -> Result<Self> {
523        let request_type = buf.read_u8()?;
524        let request = buf.read_u8()?;
525        let value = buf.read_u16::<LE>()?;
526        let index = buf.read_u16::<LE>()?;
527        let length = buf.read_u16::<LE>()?;
528        Ok(Self { request_type, request, value, index, length })
529    }
530}
531
532/// FunctionFS event type.
533pub mod event {
534    pub const BIND: u8 = 0;
535    pub const UNBIND: u8 = 1;
536    pub const ENABLE: u8 = 2;
537    pub const DISABLE: u8 = 3;
538    pub const SETUP: u8 = 4;
539    pub const SUSPEND: u8 = 5;
540    pub const RESUME: u8 = 6;
541}
542
543/// FunctionFS event.
544pub struct Event {
545    pub data: [u8; 8],
546    pub event_type: u8,
547}
548
549impl Event {
550    /// Size of raw event data.
551    pub const SIZE: usize = 12;
552
553    /// Parse FunctionFS event data.
554    pub fn parse(mut buf: &[u8]) -> Result<Self> {
555        let mut data = [0; 8];
556        buf.read_exact(&mut data)?;
557        let event_type = buf.read_u8()?;
558        let mut pad = [0; 3];
559        buf.read_exact(&mut pad)?;
560        Ok(Self { data, event_type })
561    }
562}
563
564pub const FS_TYPE: &CStr = c"functionfs";
565
566/// FunctionFS mount options.
567#[derive(Debug, Clone, Default)]
568pub struct MountOptions {
569    pub no_disconnect: bool,
570    pub rmode: Option<u32>,
571    pub fmode: Option<u32>,
572    pub mode: Option<u32>,
573    pub uid: Option<u32>,
574    pub gid: Option<u32>,
575}
576
577impl MountOptions {
578    fn to_mount_data(&self) -> String {
579        let mut opts = Vec::new();
580
581        if self.no_disconnect {
582            opts.push("no_disconnect=1".to_string());
583        }
584        if let Some(v) = self.rmode {
585            opts.push(format!("rmode={v}"));
586        }
587        if let Some(v) = self.fmode {
588            opts.push(format!("fmode={v}"));
589        }
590        if let Some(v) = self.mode {
591            opts.push(format!("mode={v}"));
592        }
593        if let Some(v) = self.uid {
594            opts.push(format!("uid={v}"));
595        }
596        if let Some(v) = self.gid {
597            opts.push(format!("gid={v}"));
598        }
599
600        opts.join(",")
601    }
602}
603
604pub fn mount(instance: &OsStr, target: &Path, opts: &MountOptions) -> std::io::Result<()> {
605    let data = CString::new(opts.to_mount_data()).unwrap();
606    rustix::mount::mount(instance, target, FS_TYPE, MountFlags::empty(), &*data)?;
607    Ok(())
608}
609
610pub fn umount(target: &Path, lazy: bool) -> std::io::Result<()> {
611    let flags = if lazy { UnmountFlags::DETACH } else { UnmountFlags::empty() };
612    rustix::mount::unmount(target, flags)?;
613    Ok(())
614}
615
616// --- FunctionFS ioctls ---
617
618pub fn fifo_status(fd: BorrowedFd<'_>) -> std::io::Result<c_int> {
619    Ok(unsafe { ioctl::ioctl(fd, ReturnInt::<{ opcode::none(b'g', 1) }>::new()) }?)
620}
621
622pub fn fifo_flush(fd: BorrowedFd<'_>) -> std::io::Result<c_int> {
623    Ok(unsafe { ioctl::ioctl(fd, ReturnInt::<{ opcode::none(b'g', 2) }>::new()) }?)
624}
625
626pub fn clear_halt(fd: BorrowedFd<'_>) -> std::io::Result<c_int> {
627    Ok(unsafe { ioctl::ioctl(fd, ReturnInt::<{ opcode::none(b'g', 3) }>::new()) }?)
628}
629
630pub fn interface_revmap(fd: BorrowedFd<'_>, val: c_int) -> std::io::Result<c_int> {
631    Ok(unsafe { ioctl::ioctl(fd, IntReturnInt::<{ opcode::none(b'g', 128) }>::new(val as usize)) }?)
632}
633
634pub fn endpoint_revmap(fd: BorrowedFd<'_>) -> std::io::Result<c_int> {
635    Ok(unsafe { ioctl::ioctl(fd, ReturnInt::<{ opcode::none(b'g', 129) }>::new()) }?)
636}
637
638pub fn endpoint_desc(fd: BorrowedFd<'_>, buf: &mut [u8; EndpointDesc::AUDIO_SIZE]) -> std::io::Result<()> {
639    unsafe {
640        ioctl::ioctl(
641            fd,
642            ioctl::Updater::<{ opcode::read::<[u8; EndpointDesc::AUDIO_SIZE]>(b'g', 130) }, _>::new(buf),
643        )
644    }?;
645    Ok(())
646}
647
648// --- FunctionFS DMA-BUF ioctls (kernel >= 6.9) ---
649
650/// `FUNCTIONFS_DMABUF_ATTACH`: `_IOW('g', 131, int)`
651pub fn dmabuf_attach(ep_fd: BorrowedFd<'_>, dmabuf_fd: RawFd) -> std::io::Result<()> {
652    unsafe { ioctl::ioctl(ep_fd, ioctl::Setter::<{ opcode::write::<c_int>(b'g', 131) }, _>::new(dmabuf_fd)) }?;
653    Ok(())
654}
655
656/// `FUNCTIONFS_DMABUF_DETACH`: `_IOW('g', 132, int)`
657pub fn dmabuf_detach(ep_fd: BorrowedFd<'_>, dmabuf_fd: RawFd) -> std::io::Result<()> {
658    unsafe { ioctl::ioctl(ep_fd, ioctl::Setter::<{ opcode::write::<c_int>(b'g', 132) }, _>::new(dmabuf_fd)) }?;
659    Ok(())
660}
661
662/// Matches the kernel's `struct usb_ffs_dmabuf_transfer_req`.
663#[repr(C, packed)]
664pub struct DmaBufTransferReq {
665    pub fd: c_int,
666    pub flags: u32,
667    pub length: u64,
668}
669
670/// `FUNCTIONFS_DMABUF_TRANSFER`: `_IOW('g', 133, struct usb_ffs_dmabuf_transfer_req)`
671pub fn dmabuf_transfer(ep_fd: BorrowedFd<'_>, req: &DmaBufTransferReq) -> std::io::Result<()> {
672    unsafe {
673        ioctl::ioctl(ep_fd, ioctl::Setter::<{ opcode::write::<DmaBufTransferReq>(b'g', 133) }, _>::new(req))
674    }?;
675    Ok(())
676}