1use std::{
4 fmt::{self, Write as FmtWrite},
5 fs::File,
6 io::{Read, Write},
7 marker::PhantomData,
8 str::FromStr,
9};
10
11use camino::{Utf8Path, Utf8PathBuf};
12use rustix::{
13 fd::{AsFd, BorrowedFd, OwnedFd},
14 fs::{openat, Dir, Mode, OFlags, CWD},
15 path::Arg,
16};
17use strum::EnumString;
18
19use crate::{
20 types::*,
21 watcher::{DevicePathStream, WatchResult, Watcher},
22 Error, Result,
23};
24
25mod sealed {
26 pub trait Sealed {}
27}
28
29use sealed::Sealed;
30
31macro_rules! impl_sealed {
32 ($ty:ty $(, forall($($args:tt)*))?) => {
33 impl $($($args)*)? Sealed for $ty {}
34 };
35}
36
37trait PropertyReader {
38 type Read;
39
40 fn read(s: &str) -> Result<Self::Read>;
41}
42
43trait PropertyWriter: PropertyReader {
44 type Write;
45
46 fn write(dest: impl Write, value: &Self::Write) -> Result<()>;
47}
48
49pub trait PropertyReadable: Sealed + fmt::Debug {
51 type Read;
52
53 fn get(&self) -> Result<Self::Read>;
55}
56
57pub trait PropertyWritable: PropertyReadable {
59 type Write;
60
61 fn set(&self, value: &Self::Write) -> Result<()>;
63}
64
65struct PropertyImpl<'fd, P: PropertyReader> {
66 dfd: BorrowedFd<'fd>,
67 path: &'static str,
68 _impl: PhantomData<P>,
69}
70
71impl_sealed!(PropertyImpl<'_, P>, forall(<P: PropertyReader>));
72
73impl<P: PropertyReader> fmt::Debug for PropertyImpl<'_, P> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(f, "(property:{})", self.path)
76 }
77}
78
79impl<'fd, P: PropertyReader> PropertyImpl<'fd, P> {
80 fn new(dfd: BorrowedFd<'fd>, path: &'static str) -> Self {
81 Self {
82 dfd,
83 path,
84 _impl: PhantomData,
85 }
86 }
87}
88
89impl<P: PropertyReader> PropertyReadable for PropertyImpl<'_, P> {
90 type Read = P::Read;
91
92 fn get(&self) -> Result<P::Read> {
93 let fd = openat(
94 self.dfd,
95 self.path,
96 OFlags::RDONLY | OFlags::CLOEXEC,
97 Mode::empty(),
98 )?;
99 let mut file = File::from(fd);
100 let mut s = "".to_owned();
101 file.read_to_string(&mut s)?;
102 s.truncate(s.trim_end().len());
103 P::read(&s)
104 }
105}
106
107impl<P: PropertyReader + PropertyWriter> PropertyWritable for PropertyImpl<'_, P> {
108 type Write = P::Write;
109
110 fn set(&self, value: &P::Write) -> Result<()> {
111 let fd = openat(
112 self.dfd,
113 self.path,
114 OFlags::RDONLY | OFlags::CLOEXEC,
115 Mode::empty(),
116 )?;
117 P::write(File::from(fd), value)?;
118 Ok(())
119 }
120}
121
122struct PropertyParse<R: FromStr> {
123 _phantom: PhantomData<R>,
124}
125
126impl<R: FromStr> PropertyReader for PropertyParse<R>
127where
128 Error: From<R::Err>,
129{
130 type Read = R;
131
132 fn read(s: &str) -> Result<R> {
133 s.parse().map_err(Error::from)
134 }
135}
136
137struct PropertyParseDisplay<R: FromStr, W: fmt::Display> {
138 _phantom: PhantomData<(R, W)>,
139}
140
141impl<R: FromStr, W: fmt::Display> PropertyReader for PropertyParseDisplay<R, W>
142where
143 Error: From<R::Err>,
144{
145 type Read = R;
146
147 fn read(s: &str) -> Result<R> {
148 PropertyParse::<R>::read(s)
149 }
150}
151
152impl<R: FromStr, W: fmt::Display> PropertyWriter for PropertyParseDisplay<R, W>
153where
154 Error: From<R::Err>,
155{
156 type Write = W;
157
158 fn write(mut dest: impl Write, value: &Self::Write) -> Result<()> {
159 write!(dest, "{value}").map_err(Into::into)
160 }
161}
162
163type PropertyRoleSelection<T> = PropertyParseDisplay<RoleSelection<T>, T>;
164
165struct PropertyHexU16;
166
167impl PropertyReader for PropertyHexU16 {
168 type Read = u16;
169
170 fn read(s: &str) -> Result<Self::Read> {
171 u16::from_str_radix(s, 16).or(Err(Error::Parse))
172 }
173}
174
175struct PropertyHexPrefixedU32;
176
177impl PropertyReader for PropertyHexPrefixedU32 {
178 type Read = u32;
179
180 fn read(s: &str) -> Result<Self::Read> {
181 let s = s.strip_prefix("0x").ok_or(Error::Parse)?;
182 u32::from_str_radix(s, 16).or(Err(Error::Parse))
183 }
184}
185
186struct PropertyBoolIntegral;
187
188impl PropertyReader for PropertyBoolIntegral {
189 type Read = bool;
190
191 fn read(s: &str) -> Result<Self::Read> {
192 let n = u32::from_str(s)?;
193 Ok(n != 0)
194 }
195}
196
197struct PropertyBoolYesNo;
198
199impl PropertyReader for PropertyBoolYesNo {
200 type Read = bool;
201
202 fn read(s: &str) -> Result<Self::Read> {
203 match s {
204 "yes" => Ok(true),
205 "no" => Ok(false),
206 _ => Err(Error::Parse),
207 }
208 }
209}
210
211impl PropertyWriter for PropertyBoolYesNo {
212 type Write = bool;
213
214 fn write(mut dest: impl Write, value: &Self::Write) -> Result<()> {
215 if *value {
216 write!(dest, "yes")?;
217 } else {
218 write!(dest, "no")?;
219 }
220 Ok(())
221 }
222}
223
224struct PropertyPreferredRole;
225
226impl PropertyReader for PropertyPreferredRole {
227 type Read = Option<PowerRole>;
228
229 fn read(s: &str) -> Result<Self::Read> {
230 if s.is_empty() {
231 return Ok(None);
232 }
233
234 Ok(Some(s.parse()?))
235 }
236}
237
238impl PropertyWriter for PropertyPreferredRole {
239 type Write = Option<PowerRole>;
240
241 fn write(mut dest: impl Write, value: &Self::Write) -> Result<()> {
242 match value {
243 Some(value) => write!(dest, "{value}")?,
244 None => write!(dest, "none")?,
245 }
246
247 Ok(())
248 }
249}
250
251macro_rules! property {
252 (
255 _stage_fill_return,
256 $name:ident,
257 ro($read:ty),
258 $($rest:tt)*
259 ) => {
260 property!(_stage_fill_with, $name,
261 read($read),
262 returns(impl PropertyReadable<Read = $read> + '_),
263 $($rest)*);
264 };
265
266 (
267 _stage_fill_return,
268 $name:ident,
269 rw($read:ty),
270 $($rest:tt)*
271 ) => {
272 property!(_stage_fill_with, $name,
273 read($read),
274 returns(impl PropertyWritable<Read = $read, Write = $read> + '_),
275 $($rest)*);
276 };
277
278 (
279 _stage_fill_return,
280 $name:ident,
281 rw($read:ty, $write:ty),
282 $($rest:tt)*
283 ) => {
284 property!(_stage_fill_with, $name,
285 read($read),
286 returns(impl PropertyWritable<Read = $read, Write = $write> + '_),
287 $($rest)*);
288 };
289
290 (
291 _stage_fill_with,
292 $name:ident,
293 read($read:ty),
294 returns($ret:ty),
295 with($impl:ty),
296 $($rest:tt)*
297 ) => {
298 property!(_stage_fill_from, $name,
299 returns($ret),
300 with($impl),
301 $($rest)*);
302 };
303
304 (
305 _stage_fill_with,
306 $name:ident,
307 read($read:ty),
308 returns($ret:ty),
309 with(),
310 $($rest:tt)*
311 ) => {
312 property!(_stage_fill_from, $name,
313 returns($ret),
314 with(PropertyParse::<$read>),
315 $($rest)*);
316 };
317
318 (
319 _stage_fill_from,
320 $name:ident,
321 returns($ret:ty),
322 with($impl:ty),
323 from($from:literal),
324 $($rest:tt)*
325 ) => {
326 property!(_stage_final, $name,
327 returns($ret),
328 with($impl),
329 from($from),
330 $($rest)*);
331 };
332
333 (
334 _stage_fill_from,
335 $name:ident,
336 returns($ret:ty),
337 with($impl:ty),
338 from(subdir($subdir:literal)),
339 $($rest:tt)*
340 ) => {
341 property!(_stage_final, $name,
342 returns($ret),
343 with($impl),
344 from(concat!($subdir, "/", stringify!($name))),
345 $($rest)*);
346 };
347
348 (
349 _stage_fill_from,
350 $name:ident,
351 returns($ret:ty),
352 with($impl:ty),
353 from(),
354 $($rest:tt)*
355 ) => {
356 property!(_stage_final, $name,
357 returns($ret),
358 with($impl),
359 from(stringify!($name)),
360 $($rest)*);
361 };
362
363 (
364 _stage_final,
365 $name:ident,
366 returns($ret:ty),
367 with($impl:ty),
368 from($from:expr),
369 doc($($doc:tt)?)
370 ) => {
371 $(#[doc = $doc])?
372 pub fn $name(&self) -> $ret {
373 PropertyImpl::<'_, $impl>::new(self.dfd.as_fd(), $from)
374 }
375 };
376
377 (
378 $name:ident,
379 $access:ident($read:ty $(, $write:ty)?)
380 $(, with($impl:ty))?
381 $(, from($($from:tt)*))?
382 $(, doc($doc:tt))?
383 $(,)?
384 ) => {
385 property!(_stage_fill_return,
386 $name,
387 $access($read $(, $write)?),
388 with($($impl)?),
389 from($($($from)*)?),
390 doc($($doc)?));
391 };
392}
393
394enum MaybeOwnedFd<'a> {
395 Owned(OwnedFd),
396 Borrowed(BorrowedFd<'a>),
397}
398
399impl AsFd for MaybeOwnedFd<'_> {
400 fn as_fd(&self) -> BorrowedFd<'_> {
401 match &self {
402 MaybeOwnedFd::Owned(fd) => fd.as_fd(),
403 MaybeOwnedFd::Borrowed(fd) => fd.as_fd(),
404 }
405 }
406}
407
408pub const SYS_CLASS_TYPEC: &str = "/sys/class/typec";
410
411pub trait DevicePath:
422 Sealed + fmt::Debug + Copy + Clone + PartialEq + Eq + std::hash::Hash + Sized + Unpin
423{
424 type Parent: DevicePathParent;
428
429 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self>;
430 fn build_basename(&self, s: &mut String);
431
432 fn parent(&self) -> Self::Parent;
433}
434
435macro_rules! device_path_child_collection_getter {
436 ($name:ident, $ret:ty $(, doc($doc:tt))? $(,)?) => {
437 $(#[doc = $doc])?
438 pub fn $name(&self) -> DevicePathCollection<$ret> {
439 DevicePathCollection { parent: *self }
440 }
441 };
442}
443
444pub trait DevicePathParent:
447 Sealed + fmt::Debug + Copy + Clone + PartialEq + Eq + std::hash::Hash + Unpin
448{
449 fn parse_syspath(p: &Utf8Path) -> Option<Self>;
450 fn build_syspath(&self, p: &mut Utf8PathBuf);
451}
452
453impl<P: DevicePath> DevicePathParent for P {
454 fn parse_syspath(p: &Utf8Path) -> Option<Self> {
455 let parent = P::Parent::parse_syspath(p.parent()?)?;
456 P::parse_basename(p.file_name()?, parent)
457 }
458
459 fn build_syspath(&self, p: &mut Utf8PathBuf) {
460 self.parent().build_syspath(p);
461
462 let mut s = "".to_owned();
463 self.build_basename(&mut s);
464 p.push(s);
465 }
466}
467
468#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
471pub struct NoParent;
472
473impl_sealed!(NoParent);
474
475impl DevicePathParent for NoParent {
476 fn parse_syspath(_p: &Utf8Path) -> Option<Self> {
477 Some(NoParent)
478 }
479
480 fn build_syspath(&self, _p: &mut Utf8PathBuf) {}
481}
482
483pub trait DevicePathIndexed: DevicePath {
485 type Index;
486
487 fn child_of(parent: Self::Parent, index: Self::Index) -> Self;
489}
490
491pub trait DevicePathWatchable: DevicePath {
493 fn any_added(ctx: &Watcher) -> WatchResult<DevicePathStream<NoParent, Self>>;
495 fn any_changed(ctx: &Watcher) -> WatchResult<DevicePathStream<NoParent, Self>>;
497 fn any_removed(ctx: &Watcher) -> WatchResult<DevicePathStream<NoParent, Self>>;
499
500 fn added(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Self, Self>>;
503 fn changed(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Self, Self>>;
506 fn removed(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Self, Self>>;
509}
510
511macro_rules! impl_device_path_watchable {
512 ($path:ty $(, forall($($args:tt)*))?, $channels:ident) => {
513 impl $($($args)*)? DevicePathWatchable for $path {
514 fn any_added(ctx: &Watcher) -> WatchResult<DevicePathStream<NoParent, Self>> {
515 ctx.with_channels(|channels, inner| channels.$channels.on_any_added.insert(NoParent, inner))
516 }
517 fn any_changed(ctx: &Watcher) -> WatchResult<DevicePathStream<NoParent, Self>> {
518 ctx.with_channels(|channels, inner| channels.$channels.on_any_changed.insert(NoParent, inner))
519 }
520 fn any_removed(ctx: &Watcher) -> WatchResult<DevicePathStream<NoParent, Self>> {
521 ctx.with_channels(|channels, inner| channels.$channels.on_any_removed.insert(NoParent, inner))
522 }
523
524 fn added(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Self, Self>> {
525 ctx.with_channels(|channels, inner| channels.$channels.on_added.insert(*self, inner))
526 }
527 fn changed(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Self, Self>> {
528 ctx.with_channels(|channels, inner| channels.$channels.on_changed.insert(*self, inner))
529 }
530 fn removed(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Self, Self>> {
531 ctx.with_channels(|channels, inner| channels.$channels.on_removed.insert(*self, inner))
532 }
533 }
534 }
535}
536
537pub trait DevicePathWatchableFromParent: DevicePathIndexed {
546 fn added_in(
548 parent: Self::Parent,
549 ctx: &Watcher,
550 ) -> WatchResult<DevicePathStream<Self::Parent, Self>>;
551 fn changed_in(
553 parent: Self::Parent,
554 ctx: &Watcher,
555 ) -> WatchResult<DevicePathStream<Self::Parent, Self>>;
556 fn removed_in(
558 parent: Self::Parent,
559 ctx: &Watcher,
560 ) -> WatchResult<DevicePathStream<Self::Parent, Self>>;
561}
562
563macro_rules! impl_device_path_watchable_from_parent {
564 ($path:ty $(, forall($($args:tt)*))?, $channels:ident) => {
565 impl_device_path_watchable!($path $(, forall($($args)*))?, $channels);
566
567 impl $($($args)*)? DevicePathWatchableFromParent for $path {
568 fn added_in(parent: Self::Parent, ctx: &Watcher) -> WatchResult<DevicePathStream<Self::Parent, Self>> {
569 ctx.with_channels(|channels, inner| channels.$channels.on_inventory_added.insert(parent, inner))
570 }
571 fn changed_in(parent: Self::Parent, ctx: &Watcher) -> WatchResult<DevicePathStream<Self::Parent, Self>> {
572 ctx.with_channels(|channels, inner| channels.$channels.on_inventory_changed.insert(parent, inner))
573 }
574 fn removed_in(parent: Self::Parent, ctx: &Watcher) -> WatchResult<DevicePathStream<Self::Parent, Self>> {
575 ctx.with_channels(|channels, inner| channels.$channels.on_inventory_removed.insert(parent, inner))
576 }
577 }
578 }
579}
580
581pub struct DevicePathCollection<Child: DevicePath> {
583 parent: Child::Parent,
584}
585
586impl<Child: DevicePath> DevicePathCollection<Child> {
587 pub fn parent(&self) -> &Child::Parent {
589 &self.parent
590 }
591}
592
593impl<Child: DevicePathIndexed> DevicePathCollection<Child> {
594 pub fn get(&self, index: Child::Index) -> Child {
596 Child::child_of(self.parent, index)
597 }
598}
599
600impl<Child: DevicePathWatchableFromParent> DevicePathCollection<Child> {
601 pub fn added(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Child::Parent, Child>> {
604 Child::added_in(self.parent, ctx)
605 }
606
607 pub fn changed(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Child::Parent, Child>> {
610 Child::changed_in(self.parent, ctx)
611 }
612
613 pub fn removed(&self, ctx: &Watcher) -> WatchResult<DevicePathStream<Child::Parent, Child>> {
616 Child::removed_in(self.parent, ctx)
617 }
618}
619
620pub trait Device: Sealed + Sized {
627 type Path: DevicePath;
628
629 fn from_fd(dfd: OwnedFd, path: Self::Path) -> Self;
630
631 fn path(&self) -> &Self::Path;
633
634 fn open(path: Self::Path) -> Result<Self> {
636 let mut sys = Utf8PathBuf::from(SYS_CLASS_TYPEC);
637 path.build_syspath(&mut sys);
638 let dfd = openat(
639 CWD,
640 sys.as_str(),
641 OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
642 Mode::empty(),
643 )?;
644 Ok(Self::from_fd(dfd, path))
645 }
646}
647
648macro_rules! impl_device {
649 ($dev:ty $(, forall($($args:tt)*))?, path($path:ty)) => {
650 impl $($($args)*)? Device for $dev {
651 type Path = $path;
652
653 fn from_fd(dfd: OwnedFd, path: Self::Path) -> Self {
654 Self { dfd, path }
655 }
656
657 fn path(&self) -> &Self::Path {
658 &self.path
659 }
660 }
661 };
662}
663
664#[derive(Debug)]
668pub struct DeviceEntry<'fd, T: Device> {
669 parent_dfd: BorrowedFd<'fd>,
670 path: T::Path,
671}
672
673impl<T: Device> DeviceEntry<'_, T> {
674 pub fn path(&self) -> &T::Path {
675 &self.path
676 }
677
678 pub fn open(&self) -> Result<T> {
680 let mut s = String::new();
681 self.path.build_basename(&mut s);
682 let dfd = openat(
683 self.parent_dfd,
684 s,
685 OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
686 Mode::empty(),
687 )?;
688 Ok(T::from_fd(dfd, self.path))
689 }
690}
691
692pub struct DeviceCollection<'fd, Child: Device> {
694 dfd: MaybeOwnedFd<'fd>,
695 parent: <Child::Path as DevicePath>::Parent,
696 phantom: PhantomData<Child>,
697}
698
699impl<Child: Device> DeviceCollection<'_, Child> {
700 pub fn iter(&self) -> Result<DeviceIter<'_, Child>> {
702 Ok(DeviceIter {
703 dfd: self.dfd.as_fd(),
704 dir: Dir::read_from(&self.dfd)?,
705 parent: self.parent,
706 phantom: PhantomData,
707 })
708 }
709
710 pub fn iter_opened(&self) -> Result<impl Iterator<Item = Result<Child>> + '_> {
712 let iter = self.iter()?;
713 Ok(iter.map(|x| x.and_then(|x| x.open())))
714 }
715
716 pub fn list(&self) -> Result<Vec<DeviceEntry<'_, Child>>> {
718 self.iter().and_then(|x| x.collect())
719 }
720
721 pub fn list_opened(&self) -> Result<Vec<Child>> {
723 self.iter_opened().and_then(|x| x.collect())
724 }
725}
726
727impl<Child: Device> DeviceCollection<'_, Child>
728where
729 Child::Path: DevicePathIndexed,
730{
731 pub fn get(&self, index: <Child::Path as DevicePathIndexed>::Index) -> Result<Child> {
732 let path = Child::Path::child_of(self.parent, index);
733 let mut s = String::new();
734 path.build_basename(&mut s);
735
736 let dfd = openat(
737 self.dfd.as_fd(),
738 s,
739 OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
740 Mode::empty(),
741 )?;
742 Ok(Child::from_fd(dfd, path))
743 }
744}
745
746pub struct DeviceIter<'fd, Child: Device> {
750 dfd: BorrowedFd<'fd>,
751 dir: Dir,
752 parent: <Child::Path as DevicePath>::Parent,
753 phantom: PhantomData<Child>,
754}
755
756impl<'fd, Child: Device> Iterator for DeviceIter<'fd, Child> {
757 type Item = Result<DeviceEntry<'fd, Child>>;
758
759 fn next(&mut self) -> Option<Self::Item> {
760 for entry in &mut self.dir {
761 let entry = match entry {
762 Ok(entry) => entry,
763 Err(err) => return Some(Err(err.into())),
764 };
765
766 let name = entry.file_name();
767 let name = match name.as_str() {
768 Ok(name) => name,
769 Err(err) => return Some(Err(err.into())),
770 };
771
772 let Some(path) = Child::Path::parse_basename(name, self.parent) else {
773 continue;
774 };
775
776 if path.parent() != self.parent {
777 continue;
778 }
779
780 return Some(Ok(DeviceEntry {
781 parent_dfd: self.dfd,
782 path,
783 }));
784 }
785
786 None
787 }
788}
789
790#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
792pub struct PortPath {
793 pub port: u32,
795}
796
797impl PortPath {
798 pub fn collection() -> DevicePathCollection<PortPath> {
799 DevicePathCollection { parent: NoParent }
800 }
801
802 pub fn cable(&self) -> CablePath {
804 CablePath { port: self.port }
805 }
806
807 pub fn partner(&self) -> PartnerPath {
809 PartnerPath { port: self.port }
810 }
811
812 device_path_child_collection_getter!(
813 alt_modes,
814 AltModePath<PortPath>,
815 doc("Returns a path collection for this port's alternate modes."),
816 );
817}
818
819impl_sealed!(PortPath);
820
821impl DevicePath for PortPath {
822 type Parent = NoParent;
823
824 fn parse_basename(s: &str, _parent: Self::Parent) -> Option<Self> {
825 let s = s.strip_prefix("port")?;
826 let port = u32::from_str(s).ok()?;
827 Some(Self { port })
828 }
829
830 fn build_basename(&self, s: &mut String) {
831 write!(s, "port{}", self.port).unwrap();
832 }
833
834 fn parent(&self) -> Self::Parent {
835 NoParent
836 }
837}
838
839impl DevicePathIndexed for PortPath {
840 type Index = u32;
841
842 fn child_of(_parent: Self::Parent, index: Self::Index) -> Self {
843 PortPath { port: index }
844 }
845}
846
847impl_device_path_watchable_from_parent!(PortPath, port);
848
849#[derive(Debug)]
851pub struct Port {
852 dfd: OwnedFd,
853 path: PortPath,
854}
855
856impl_sealed!(Port);
857impl_device!(Port, path(PortPath));
858
859impl Port {
860 pub fn collection() -> Result<DeviceCollection<'static, Port>> {
861 let dfd = openat(
862 CWD,
863 SYS_CLASS_TYPEC,
864 OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
865 Mode::empty(),
866 )?;
867 Ok(DeviceCollection {
868 dfd: MaybeOwnedFd::Owned(dfd),
869 parent: NoParent,
870 phantom: PhantomData,
871 })
872 }
873
874 property!(
875 data_role,
876 rw(RoleSelection<DataRole>, DataRole),
877 with(PropertyRoleSelection::<DataRole>),
878 doc("The port's currently selected role in data transmission."),
879 );
880 property!(
881 port_type,
882 rw(RoleSelection<PortType>, PortType),
883 with(PropertyRoleSelection::<PortType>),
884 doc("The port's currently selected type."),
885 );
886 property!(
887 power_role,
888 rw(RoleSelection<PowerRole>, PowerRole),
889 with(PropertyRoleSelection::<PowerRole>),
890 doc("The port's currently selected role in power transmission."),
891 );
892 property!(
893 preferred_role,
894 ro(Option<PowerRole>),
895 with(PropertyPreferredRole),
896 doc("If this port is dual-role, then its preferred role of the two."),
897 );
898 property!(power_operation_mode, ro(PowerOperationMode));
899 property!(usb_power_delivery_revision, ro(Revision));
900 property!(usb_typec_revision, ro(Revision));
901
902 pub fn alt_modes(&self) -> DeviceCollection<'_, AltMode<PortPath>> {
904 DeviceCollection {
905 dfd: MaybeOwnedFd::Borrowed(self.dfd.as_fd()),
906 parent: self.path,
907 phantom: PhantomData,
908 }
909 }
910
911 pub fn cable(&self) -> DeviceEntry<'_, Cable> {
913 DeviceEntry {
914 parent_dfd: self.dfd.as_fd(),
915 path: self.path.cable(),
916 }
917 }
918
919 pub fn partner(&self) -> DeviceEntry<'_, Partner> {
921 DeviceEntry {
922 parent_dfd: self.dfd.as_fd(),
923 path: self.path.partner(),
924 }
925 }
926}
927
928#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
930pub struct PartnerPath {
931 pub port: u32,
933}
934
935impl PartnerPath {
936 device_path_child_collection_getter!(
937 alt_modes,
938 AltModePath<PartnerPath>,
939 doc("Returns a path collection for this partner's alternate modes."),
940 );
941 device_path_child_collection_getter!(
942 pds,
943 PowerDeliveryPath,
944 doc("Returns a path collection for this partner's power delivery devices."),
945 );
946}
947
948impl_sealed!(PartnerPath);
949
950impl DevicePath for PartnerPath {
951 type Parent = PortPath;
952
953 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self> {
954 let s = s.strip_suffix("-partner")?;
955 let parent = Self::Parent::parse_basename(s, parent.parent())?;
956 Some(Self { port: parent.port })
957 }
958
959 fn build_basename(&self, s: &mut String) {
960 self.parent().build_basename(s);
961 write!(s, "-partner").unwrap();
962 }
963
964 fn parent(&self) -> Self::Parent {
965 Self::Parent { port: self.port }
966 }
967}
968
969impl_device_path_watchable!(PartnerPath, partner);
970
971#[derive(Debug)]
973pub struct Partner {
974 dfd: OwnedFd,
975 path: PartnerPath,
976}
977
978impl_sealed!(Partner);
979impl_device!(Partner, path(PartnerPath));
980
981impl Partner {
982 property!(usb_power_delivery_revision, ro(Revision));
985
986 pub fn identity(&self) -> IdentityPartner<'_> {
988 IdentityPartner {
989 dfd: self.dfd.as_fd(),
990 }
991 }
992
993 pub fn alt_modes(&self) -> DeviceCollection<'_, AltMode<PartnerPath>> {
995 DeviceCollection {
996 dfd: MaybeOwnedFd::Borrowed(self.dfd.as_fd()),
997 parent: self.path,
998 phantom: PhantomData,
999 }
1000 }
1001
1002 pub fn pds(&self) -> DeviceCollection<'_, PowerDelivery> {
1004 DeviceCollection {
1005 dfd: MaybeOwnedFd::Borrowed(self.dfd.as_fd()),
1006 parent: self.path,
1007 phantom: PhantomData,
1008 }
1009 }
1010}
1011
1012#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1014pub struct CablePath {
1015 pub port: u32,
1017}
1018
1019impl CablePath {
1020 device_path_child_collection_getter!(
1021 plugs,
1022 PlugPath,
1023 doc("Returns a path collection for this cable's plugs."),
1024 );
1025}
1026
1027impl_sealed!(CablePath);
1028
1029impl DevicePath for CablePath {
1030 type Parent = PortPath;
1031
1032 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self> {
1033 let s = s.strip_suffix("-cable")?;
1034 let parent = Self::Parent::parse_basename(s, parent.parent())?;
1035 Some(Self { port: parent.port })
1036 }
1037
1038 fn build_basename(&self, s: &mut String) {
1039 self.parent().build_basename(s);
1040 write!(s, "-cable").unwrap();
1041 }
1042
1043 fn parent(&self) -> Self::Parent {
1044 Self::Parent { port: self.port }
1045 }
1046}
1047
1048impl_device_path_watchable!(CablePath, cable);
1049
1050#[derive(Debug)]
1052pub struct Cable {
1053 dfd: OwnedFd,
1054 path: CablePath,
1055}
1056
1057impl_sealed!(Cable);
1058impl_device!(Cable, path(CablePath));
1059
1060impl Cable {
1061 pub fn identity(&self) -> IdentityCable<'_> {
1063 IdentityCable {
1064 dfd: self.dfd.as_fd(),
1065 }
1066 }
1067
1068 pub fn plugs(&self) -> DeviceCollection<'_, Plug> {
1070 DeviceCollection {
1071 dfd: MaybeOwnedFd::Borrowed(self.dfd.as_fd()),
1072 parent: self.path,
1073 phantom: PhantomData,
1074 }
1075 }
1076
1077 property!(cable_type, ro(CableType), from("type"));
1078 property!(plug_type, ro(PlugType));
1079 property!(usb_power_delivery_revision, ro(Revision));
1080}
1081
1082#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1084pub struct PlugPath {
1085 pub port: u32,
1087 pub plug: u32,
1089}
1090
1091impl PlugPath {
1092 device_path_child_collection_getter!(
1093 alt_modes,
1094 AltModePath<PlugPath>,
1095 doc("Returns a path collection for this plug's alternate modes."),
1096 );
1097}
1098
1099impl_sealed!(PlugPath);
1100
1101impl DevicePath for PlugPath {
1102 type Parent = CablePath;
1103
1104 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self> {
1105 let (a, b) = s.split_once('-')?;
1106 let parent =
1107 <Self::Parent as DevicePath>::Parent::parse_basename(a, parent.parent().parent())?;
1108
1109 let b = b.strip_prefix("plug")?;
1110 let plug = u32::from_str(b).ok()?;
1111
1112 Some(Self {
1113 port: parent.port,
1114 plug,
1115 })
1116 }
1117
1118 fn build_basename(&self, s: &mut String) {
1119 self.parent().parent().build_basename(s);
1120 write!(s, "-plug{}", self.plug).unwrap();
1121 }
1122
1123 fn parent(&self) -> Self::Parent {
1124 Self::Parent { port: self.port }
1125 }
1126}
1127
1128impl DevicePathIndexed for PlugPath {
1129 type Index = u32;
1130
1131 fn child_of(parent: Self::Parent, index: Self::Index) -> Self {
1132 PlugPath {
1133 port: parent.port,
1134 plug: index,
1135 }
1136 }
1137}
1138
1139impl_device_path_watchable_from_parent!(PlugPath, plug);
1140
1141#[derive(Debug)]
1143pub struct Plug {
1144 dfd: OwnedFd,
1145 path: PlugPath,
1146}
1147
1148impl_sealed!(Plug);
1149impl_device!(Plug, path(PlugPath));
1150
1151impl Plug {
1152 pub fn alt_modes(&self) -> DeviceCollection<'_, AltMode<PlugPath>> {
1154 DeviceCollection {
1155 dfd: MaybeOwnedFd::Borrowed(self.dfd.as_fd()),
1156 parent: self.path,
1157 phantom: PhantomData,
1158 }
1159 }
1160}
1161
1162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1164pub struct AltModePath<Parent: DevicePath> {
1165 pub parent: Parent,
1167 pub index: u32,
1169}
1170
1171impl_sealed!(AltModePath<Parent>, forall(<Parent: DevicePath>));
1172
1173impl<Parent: DevicePath> DevicePath for AltModePath<Parent> {
1174 type Parent = Parent;
1175
1176 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self> {
1177 let (a, b) = s.split_once('.')?;
1178
1179 let parent = Parent::parse_basename(a, parent.parent())?;
1180 let index = u32::from_str(b).ok()?;
1181
1182 Some(AltModePath { parent, index })
1183 }
1184
1185 fn build_basename(&self, s: &mut String) {
1186 self.parent().build_basename(s);
1187 write!(s, ".{}", self.index).unwrap();
1188 }
1189
1190 fn parent(&self) -> Self::Parent {
1191 self.parent
1192 }
1193}
1194
1195impl<Parent: DevicePath> DevicePathIndexed for AltModePath<Parent> {
1196 type Index = u32;
1197
1198 fn child_of(parent: Self::Parent, index: Self::Index) -> Self {
1199 Self { parent, index }
1200 }
1201}
1202
1203impl_device_path_watchable_from_parent!(AltModePath<PartnerPath>, partner_alt_mode);
1204impl_device_path_watchable_from_parent!(AltModePath<PlugPath>, plug_alt_mode);
1205
1206#[derive(Debug)]
1208pub struct AltMode<Parent: DevicePath> {
1209 dfd: OwnedFd,
1210 path: AltModePath<Parent>,
1211}
1212
1213impl_sealed!(AltMode<Parent>, forall(<Parent: DevicePath>));
1214impl_device!(AltMode<Parent>, forall(<Parent: DevicePath>), path(AltModePath<Parent>));
1215
1216impl<Parent: DevicePath> AltMode<Parent> {
1217 property!(
1218 active,
1219 rw(bool),
1220 with(PropertyBoolYesNo),
1221 doc("Is this alternate mode currently active?"),
1222 );
1223 property!(mode, ro(u32));
1224 property!(
1225 svid,
1226 ro(u16),
1227 with(PropertyHexU16),
1228 doc("A Standard or Vendor ID used to identity this mode"),
1229 );
1230 property!(vdo, ro(u32), with(PropertyHexPrefixedU32));
1231}
1232
1233impl AltMode<PortPath> {
1234 property!(
1235 supported_roles,
1236 ro(SupportedRoles),
1237 doc("The roles supported by this alternate mode"),
1238 );
1239}
1240
1241#[derive(Debug)]
1243pub struct IdentityPartner<'fd> {
1244 dfd: BorrowedFd<'fd>,
1245}
1246
1247impl IdentityPartner<'_> {
1248 property!(
1249 id_header,
1250 ro(VdoIdHeaderPartner),
1251 from(subdir("identity")),
1252 doc("The identity header.
1253
1254If this property is not available yet, then calling [`PropertyReadable::get`]
1255will return [`Error::IdentityUnavailable`]."),
1256 );
1257 property!(
1258 cert_stat,
1259 ro(VdoCertStat),
1260 from(subdir("identity")),
1261 doc("The XID from a USB-IF certified device.
1262
1263If this property is not available, either because it has not been determined yet
1264or the device is not USB-IF certified, then calling [`PropertyReadable::get`]
1265will return [`Error::IdentityUnavailable`]."),
1266 );
1267 property!(
1268 product,
1269 ro(VdoProduct),
1270 from(subdir("identity")),
1271 doc("The product IDs.
1272
1273If this property is not available yet, then calling [`PropertyReadable::get`]
1274will return [`Error::IdentityUnavailable`]."),
1275 );
1276
1277 property!(
1279 product_type_vdo1,
1280 ro(u32),
1281 with(PropertyHexPrefixedU32),
1282 from(subdir("identity"))
1283 );
1284 property!(
1285 product_type_vdo2,
1286 ro(u32),
1287 with(PropertyHexPrefixedU32),
1288 from(subdir("identity"))
1289 );
1290 property!(
1291 product_type_vdo3,
1292 ro(u32),
1293 with(PropertyHexPrefixedU32),
1294 from(subdir("identity"))
1295 );
1296}
1297
1298#[derive(Debug)]
1300pub struct IdentityCable<'fd> {
1301 dfd: BorrowedFd<'fd>,
1302}
1303
1304impl IdentityCable<'_> {
1305 property!(
1306 id_header,
1307 ro(VdoIdHeaderCable),
1308 from(subdir("identity")),
1309 doc("The identity header.
1310
1311If this property is not available yet, then calling [`PropertyReadable::get`]
1312will return [`Error::IdentityUnavailable`]."),
1313 );
1314 property!(
1315 cert_stat,
1316 ro(VdoCertStat),
1317 from(subdir("identity")),
1318 doc("The XID from a USB-IF certified device.
1319
1320If this property is not available, either because it has not been determined yet
1321or the device is not USB-IF certified, then calling [`PropertyReadable::get`]
1322will return [`Error::IdentityUnavailable`]."),
1323 );
1324 property!(
1325 product,
1326 ro(VdoProduct),
1327 from(subdir("identity")),
1328 doc("The product IDs.
1329
1330If this property is not available yet, then calling [`PropertyReadable::get`]
1331will return [`Error::IdentityUnavailable`]."),
1332 );
1333 property!(
1335 product_type_vdo1,
1336 ro(u32),
1337 with(PropertyHexPrefixedU32),
1338 from(subdir("identity"))
1339 );
1340 property!(
1341 product_type_vdo2,
1342 ro(u32),
1343 with(PropertyHexPrefixedU32),
1344 from(subdir("identity"))
1345 );
1346 property!(
1347 product_type_vdo3,
1348 ro(u32),
1349 with(PropertyHexPrefixedU32),
1350 from(subdir("identity"))
1351 );
1352}
1353
1354#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1356pub struct PowerDeliveryPath {
1357 pub port: u32,
1359 pub pd: u32,
1361}
1362
1363impl PowerDeliveryPath {
1364 pub fn source_capabilities(&self) -> CapabilitiesPath {
1366 CapabilitiesPath {
1367 port: self.port,
1368 pd: self.pd,
1369 role: PowerRole::Source,
1370 }
1371 }
1372
1373 pub fn sink_capabilities(&self) -> CapabilitiesPath {
1375 CapabilitiesPath {
1376 port: self.port,
1377 pd: self.pd,
1378 role: PowerRole::Sink,
1379 }
1380 }
1381}
1382
1383impl_sealed!(PowerDeliveryPath);
1384
1385impl DevicePath for PowerDeliveryPath {
1386 type Parent = PartnerPath;
1387
1388 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self> {
1389 let s = s.strip_prefix("pd")?;
1390 let pd = u32::from_str(s).ok()?;
1391
1392 Some(Self {
1393 port: parent.port,
1394 pd,
1395 })
1396 }
1397
1398 fn build_basename(&self, s: &mut String) {
1399 write!(s, "pd{}", self.pd).unwrap();
1400 }
1401
1402 fn parent(&self) -> Self::Parent {
1403 Self::Parent { port: self.port }
1404 }
1405}
1406
1407impl DevicePathIndexed for PowerDeliveryPath {
1408 type Index = u32;
1409
1410 fn child_of(parent: Self::Parent, index: Self::Index) -> Self {
1411 Self {
1412 port: parent.port,
1413 pd: index,
1414 }
1415 }
1416}
1417
1418impl_device_path_watchable_from_parent!(PowerDeliveryPath, pd);
1419
1420#[derive(Debug)]
1422pub struct PowerDelivery {
1423 dfd: OwnedFd,
1424 path: PowerDeliveryPath,
1425}
1426
1427impl_sealed!(PowerDelivery);
1428impl_device!(PowerDelivery, path(PowerDeliveryPath));
1429
1430impl PowerDelivery {
1431 pub fn source_capabilities(&self) -> DeviceEntry<'_, SourceCapabilities> {
1433 DeviceEntry {
1434 parent_dfd: self.dfd.as_fd(),
1435 path: self.path.source_capabilities(),
1436 }
1437 }
1438
1439 pub fn sink_capabilities(&self) -> DeviceEntry<'_, SinkCapabilities> {
1441 DeviceEntry {
1442 parent_dfd: self.dfd.as_fd(),
1443 path: self.path.sink_capabilities(),
1444 }
1445 }
1446}
1447
1448#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1450pub struct CapabilitiesPath {
1451 pub port: u32,
1453 pub pd: u32,
1455 pub role: PowerRole,
1457}
1458
1459impl CapabilitiesPath {
1460 pub fn vsafe5v(&self) -> PdoPath {
1462 self.pdos().get((1, SupplyKind::FixedSupply))
1463 }
1464
1465 device_path_child_collection_getter!(
1466 pdos,
1467 PdoPath,
1468 doc("Returns a path collection for the capabilities' PDOs."),
1469 );
1470}
1471
1472impl_sealed!(CapabilitiesPath);
1473
1474impl DevicePath for CapabilitiesPath {
1475 type Parent = PowerDeliveryPath;
1476
1477 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self> {
1478 let s = s.strip_suffix("-capabilities")?;
1479 let role = PowerRole::from_str(s).ok()?;
1480 Some(Self {
1481 port: parent.port,
1482 pd: parent.pd,
1483 role,
1484 })
1485 }
1486
1487 fn build_basename(&self, s: &mut String) {
1488 write!(s, "{}-capabilities", self.role).unwrap();
1489 }
1490
1491 fn parent(&self) -> Self::Parent {
1492 Self::Parent {
1493 port: self.port,
1494 pd: self.pd,
1495 }
1496 }
1497}
1498
1499#[derive(Debug)]
1501pub struct SourceCapabilities {
1502 dfd: OwnedFd,
1503 path: CapabilitiesPath,
1504}
1505
1506impl_sealed!(SourceCapabilities);
1507impl_device!(SourceCapabilities, path(CapabilitiesPath));
1508
1509impl SourceCapabilities {
1510 pub fn vsafe5v(&self) -> DeviceEntry<'_, SourcePdoFixedSupplyVSafe5V> {
1512 DeviceEntry {
1513 parent_dfd: self.dfd.as_fd(),
1514 path: self.path().vsafe5v(),
1515 }
1516 }
1517
1518 pub fn pdos(&self) -> DeviceCollection<'_, SourcePdo> {
1520 DeviceCollection {
1521 dfd: MaybeOwnedFd::Borrowed(self.dfd.as_fd()),
1522 parent: self.path,
1523 phantom: PhantomData,
1524 }
1525 }
1526}
1527
1528#[derive(Debug)]
1531pub struct SinkCapabilities {
1532 dfd: OwnedFd,
1533 path: CapabilitiesPath,
1534}
1535
1536impl_sealed!(SinkCapabilities);
1537impl_device!(SinkCapabilities, path(CapabilitiesPath));
1538
1539impl SinkCapabilities {
1540 pub fn vsafe5v(&self) -> DeviceEntry<'_, SinkPdoFixedSupplyVSafe5V> {
1542 DeviceEntry {
1543 parent_dfd: self.dfd.as_fd(),
1544 path: self.path().vsafe5v(),
1545 }
1546 }
1547
1548 pub fn pdos(&self) -> DeviceCollection<'_, SinkPdo> {
1550 DeviceCollection {
1551 dfd: MaybeOwnedFd::Borrowed(self.dfd.as_fd()),
1552 parent: self.path,
1553 phantom: PhantomData,
1554 }
1555 }
1556}
1557
1558#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, strum::Display)]
1560#[strum(serialize_all = "snake_case")]
1561pub enum SupplyKind {
1562 FixedSupply,
1564 Battery,
1566 VariableSupply,
1568 ProgrammableSupply,
1570}
1571
1572#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1574pub struct PdoPath {
1575 pub port: u32,
1577 pub pd: u32,
1579
1580 pub role: PowerRole,
1582 pub index: u32,
1584 pub supply: SupplyKind,
1586}
1587
1588impl_sealed!(PdoPath);
1589
1590impl DevicePath for PdoPath {
1591 type Parent = CapabilitiesPath;
1592
1593 fn parse_basename(s: &str, parent: Self::Parent) -> Option<Self> {
1594 let (a, b) = s.split_once(':')?;
1595 let index = u32::from_str(a).ok()?;
1596 let supply = SupplyKind::from_str(b).ok()?;
1597 Some(Self {
1598 port: parent.port,
1599 pd: parent.pd,
1600 role: parent.role,
1601 index,
1602 supply,
1603 })
1604 }
1605
1606 fn build_basename(&self, s: &mut String) {
1607 write!(s, "{}:{}", self.index, self.supply).unwrap();
1608 }
1609
1610 fn parent(&self) -> Self::Parent {
1611 Self::Parent {
1612 port: self.port,
1613 pd: self.pd,
1614 role: self.role,
1615 }
1616 }
1617}
1618
1619impl DevicePathIndexed for PdoPath {
1620 type Index = (u32, SupplyKind);
1621
1622 fn child_of(parent: Self::Parent, index: Self::Index) -> Self {
1623 PdoPath {
1624 port: parent.port,
1625 pd: parent.pd,
1626 role: parent.role,
1627 index: index.0,
1628 supply: index.1,
1629 }
1630 }
1631}
1632
1633#[derive(Debug)]
1635pub enum SourcePdo {
1636 FixedSupply(SourcePdoFixedSupply),
1637 Battery(SourcePdoBattery),
1638 VariableSupply(SourcePdoVariableSupply),
1639 ProgrammableSupply(SourcePdoProgrammableSupply),
1640}
1641
1642impl_sealed!(SourcePdo);
1643
1644impl Device for SourcePdo {
1645 type Path = PdoPath;
1646
1647 fn from_fd(dfd: OwnedFd, path: Self::Path) -> Self {
1648 match path.supply {
1649 SupplyKind::FixedSupply => SourcePdo::FixedSupply(SourcePdoFixedSupply { dfd, path }),
1650 SupplyKind::Battery => SourcePdo::Battery(SourcePdoBattery { dfd, path }),
1651 SupplyKind::VariableSupply => {
1652 SourcePdo::VariableSupply(SourcePdoVariableSupply { dfd, path })
1653 }
1654 SupplyKind::ProgrammableSupply => {
1655 SourcePdo::ProgrammableSupply(SourcePdoProgrammableSupply { dfd, path })
1656 }
1657 }
1658 }
1659
1660 fn path(&self) -> &Self::Path {
1661 match self {
1662 SourcePdo::FixedSupply(pdo) => pdo.path(),
1663 SourcePdo::Battery(pdo) => pdo.path(),
1664 SourcePdo::VariableSupply(pdo) => pdo.path(),
1665 SourcePdo::ProgrammableSupply(pdo) => pdo.path(),
1666 }
1667 }
1668}
1669
1670#[derive(Debug)]
1672pub enum SinkPdo {
1673 FixedSupply(SinkPdoFixedSupply),
1674 Battery(SinkPdoBattery),
1675 VariableSupply(SinkPdoVariableSupply),
1676 ProgrammableSupply(SinkPdoProgrammableSupply),
1677}
1678
1679impl_sealed!(SinkPdo);
1680
1681impl Device for SinkPdo {
1682 type Path = PdoPath;
1683
1684 fn from_fd(dfd: OwnedFd, path: Self::Path) -> Self {
1685 match path.supply {
1686 SupplyKind::FixedSupply => SinkPdo::FixedSupply(SinkPdoFixedSupply { dfd, path }),
1687 SupplyKind::Battery => SinkPdo::Battery(SinkPdoBattery { dfd, path }),
1688 SupplyKind::VariableSupply => {
1689 SinkPdo::VariableSupply(SinkPdoVariableSupply { dfd, path })
1690 }
1691 SupplyKind::ProgrammableSupply => {
1692 SinkPdo::ProgrammableSupply(SinkPdoProgrammableSupply { dfd, path })
1693 }
1694 }
1695 }
1696
1697 fn path(&self) -> &Self::Path {
1698 match self {
1699 SinkPdo::FixedSupply(pdo) => pdo.path(),
1700 SinkPdo::Battery(pdo) => pdo.path(),
1701 SinkPdo::VariableSupply(pdo) => pdo.path(),
1702 SinkPdo::ProgrammableSupply(pdo) => pdo.path(),
1703 }
1704 }
1705}
1706
1707#[derive(Debug)]
1711pub struct SourcePdoFixedSupplyVSafe5V {
1712 dfd: OwnedFd,
1713 path: PdoPath,
1714}
1715
1716impl_sealed!(SourcePdoFixedSupplyVSafe5V);
1717impl_device!(SourcePdoFixedSupplyVSafe5V, path(PdoPath));
1718
1719impl SourcePdoFixedSupplyVSafe5V {
1720 property!(dual_role_power, ro(bool), with(PropertyBoolIntegral));
1721 property!(usb_suspend_supported, ro(bool), with(PropertyBoolIntegral));
1722 property!(unconstrained_power, ro(bool), with(PropertyBoolIntegral));
1723 property!(
1724 usb_communication_capable,
1725 ro(bool),
1726 with(PropertyBoolIntegral)
1727 );
1728 property!(dual_role_data, ro(bool), with(PropertyBoolIntegral));
1729 property!(
1730 unchunked_extended_messages_supported,
1731 ro(bool),
1732 with(PropertyBoolIntegral)
1733 );
1734
1735 property!(peak_current, ro(PeakCurrent));
1737 property!(voltage, ro(Millivolts));
1738 property!(maximum_current, ro(Milliamps));
1739
1740 pub fn into_fixed_supply(self) -> SourcePdoFixedSupply {
1742 SourcePdoFixedSupply {
1743 dfd: self.dfd,
1744 path: self.path,
1745 }
1746 }
1747}
1748
1749#[derive(Debug)]
1751pub struct SourcePdoFixedSupply {
1752 dfd: OwnedFd,
1753 path: PdoPath,
1754}
1755
1756impl_sealed!(SourcePdoFixedSupply);
1757impl_device!(SourcePdoFixedSupply, path(PdoPath));
1758
1759impl SourcePdoFixedSupply {
1760 property!(peak_current, ro(PeakCurrent));
1761 property!(voltage, ro(Millivolts));
1762 property!(maximum_current, ro(Milliamps));
1763
1764 pub fn try_into_vsafe5v(self) -> std::result::Result<SourcePdoFixedSupplyVSafe5V, Self> {
1770 if self.path().index == 1 {
1771 Ok(SourcePdoFixedSupplyVSafe5V {
1772 dfd: self.dfd,
1773 path: self.path,
1774 })
1775 } else {
1776 Err(self)
1777 }
1778 }
1779}
1780
1781#[derive(Debug)]
1785pub struct SinkPdoFixedSupplyVSafe5V {
1786 dfd: OwnedFd,
1787 path: PdoPath,
1788}
1789
1790impl_sealed!(SinkPdoFixedSupplyVSafe5V);
1791impl_device!(SinkPdoFixedSupplyVSafe5V, path(PdoPath));
1792
1793impl SinkPdoFixedSupplyVSafe5V {
1794 property!(dual_role_power, ro(bool), with(PropertyBoolIntegral));
1795 property!(higher_capability, ro(bool), with(PropertyBoolIntegral));
1796 property!(unconstrained_power, ro(bool), with(PropertyBoolIntegral));
1797 property!(
1798 usb_communication_capable,
1799 ro(bool),
1800 with(PropertyBoolIntegral)
1801 );
1802 property!(dual_role_data, ro(bool), with(PropertyBoolIntegral));
1803 property!(
1804 unchunked_extended_messages_supported,
1805 ro(bool),
1806 with(PropertyBoolIntegral)
1807 );
1808 property!(fast_role_swap_current, ro(FastRoleSwapCurrent));
1809
1810 property!(voltage, ro(Millivolts));
1812 property!(operational_current, ro(Milliamps));
1813
1814 pub fn into_fixed_supply(self) -> SinkPdoFixedSupply {
1816 SinkPdoFixedSupply {
1817 dfd: self.dfd,
1818 path: self.path,
1819 }
1820 }
1821}
1822
1823#[derive(Debug)]
1825pub struct SinkPdoFixedSupply {
1826 dfd: OwnedFd,
1827 path: PdoPath,
1828}
1829
1830impl_sealed!(SinkPdoFixedSupply);
1831impl_device!(SinkPdoFixedSupply, path(PdoPath));
1832
1833impl SinkPdoFixedSupply {
1834 property!(voltage, ro(Millivolts));
1835 property!(operational_current, ro(Milliamps));
1836
1837 pub fn try_into_vsafe5v(self) -> std::result::Result<SinkPdoFixedSupplyVSafe5V, Self> {
1843 if self.path().index == 1 {
1844 Ok(SinkPdoFixedSupplyVSafe5V {
1845 dfd: self.dfd,
1846 path: self.path,
1847 })
1848 } else {
1849 Err(self)
1850 }
1851 }
1852}
1853
1854#[derive(Debug)]
1856pub struct SourcePdoBattery {
1857 dfd: OwnedFd,
1858 path: PdoPath,
1859}
1860
1861impl_sealed!(SourcePdoBattery);
1862impl_device!(SourcePdoBattery, path(PdoPath));
1863
1864impl SourcePdoBattery {
1865 property!(maximum_voltage, ro(Millivolts));
1866 property!(minimum_voltage, ro(Millivolts));
1867 property!(maximum_power, ro(Milliwatts));
1868}
1869
1870#[derive(Debug)]
1872pub struct SinkPdoBattery {
1873 dfd: OwnedFd,
1874 path: PdoPath,
1875}
1876
1877impl_sealed!(SinkPdoBattery);
1878impl_device!(SinkPdoBattery, path(PdoPath));
1879
1880impl SinkPdoBattery {
1881 property!(maximum_voltage, ro(Millivolts));
1882 property!(minimum_voltage, ro(Millivolts));
1883 property!(operational_power, ro(Milliwatts));
1884}
1885
1886#[derive(Debug)]
1888pub struct SourcePdoVariableSupply {
1889 dfd: OwnedFd,
1890 path: PdoPath,
1891}
1892
1893impl_sealed!(SourcePdoVariableSupply);
1894impl_device!(SourcePdoVariableSupply, path(PdoPath));
1895
1896impl SourcePdoVariableSupply {
1897 property!(maximum_voltage, ro(Millivolts));
1898 property!(minimum_voltage, ro(Millivolts));
1899 property!(maximum_current, ro(Milliamps));
1900}
1901
1902#[derive(Debug)]
1904pub struct SinkPdoVariableSupply {
1905 dfd: OwnedFd,
1906 path: PdoPath,
1907}
1908
1909impl_sealed!(SinkPdoVariableSupply);
1910impl_device!(SinkPdoVariableSupply, path(PdoPath));
1911
1912impl SinkPdoVariableSupply {
1913 property!(maximum_voltage, ro(Millivolts));
1914 property!(minimum_voltage, ro(Millivolts));
1915 property!(operational_current, ro(Milliamps));
1916}
1917
1918#[derive(Debug)]
1920pub struct SourcePdoProgrammableSupply {
1921 dfd: OwnedFd,
1922 path: PdoPath,
1923}
1924
1925impl_sealed!(SourcePdoProgrammableSupply);
1926impl_device!(SourcePdoProgrammableSupply, path(PdoPath));
1927
1928impl SourcePdoProgrammableSupply {
1929 property!(power_limited, ro(bool), with(PropertyBoolIntegral));
1930 property!(maximum_voltage, ro(Millivolts));
1931 property!(minimum_voltage, ro(Millivolts));
1932 property!(maximum_current, ro(Milliamps));
1933}
1934
1935#[derive(Debug)]
1937pub struct SinkPdoProgrammableSupply {
1938 dfd: OwnedFd,
1939 path: PdoPath,
1940}
1941
1942impl_sealed!(SinkPdoProgrammableSupply);
1943impl_device!(SinkPdoProgrammableSupply, path(PdoPath));
1944
1945impl SinkPdoProgrammableSupply {
1946 property!(maximum_voltage, ro(Millivolts));
1947 property!(minimum_voltage, ro(Millivolts));
1948 property!(maximum_current, ro(Milliamps));
1949}