1use 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
61pub const DIR_OUT: u8 = 0x00;
63pub const DIR_IN: u8 = 0x80;
65
66pub 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)?; 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#[derive(Clone, Debug)]
223pub struct EndpointDesc {
224 pub endpoint_address: u8,
226 pub attributes: u8,
228 pub max_packet_size: u16,
230 pub interval: u8,
232 pub audio: Option<AudioEndpointDesc>,
234}
235
236#[derive(Clone, Debug)]
238pub struct AudioEndpointDesc {
239 pub refresh: u8,
241 pub synch_address: u8,
243}
244
245impl EndpointDesc {
246 pub const TYPE: u8 = 0x05;
248
249 pub const SIZE: usize = 7;
251 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 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)?; 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)?; data.write_u16::<LE>(4)?; 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)?; data.write_u16::<LE>(5)?; 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)?; 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#[derive(Clone, Debug)]
450pub struct CustomDesc {
451 pub descriptor_type: u8,
453 pub data: Vec<u8>,
455}
456
457impl CustomDesc {
458 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)?; 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#[derive(Clone, Debug)]
507pub struct CtrlReq {
508 pub request_type: u8,
510 pub request: u8,
512 pub value: u16,
514 pub index: u16,
516 pub length: u16,
518}
519
520impl CtrlReq {
521 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
532pub 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
543pub struct Event {
545 pub data: [u8; 8],
546 pub event_type: u8,
547}
548
549impl Event {
550 pub const SIZE: usize = 12;
552
553 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#[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
616pub 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
648pub 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
656pub 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#[repr(C, packed)]
664pub struct DmaBufTransferReq {
665 pub fd: c_int,
666 pub flags: u32,
667 pub length: u64,
668}
669
670pub 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}