pci/
lib.rs

1// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright 2018 The Chromium OS Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE-BSD-3-Clause file.
5//
6// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
7
8//! Implements pci devices and busses.
9
10extern crate log;
11
12use std::fmt::{self, Debug, Display};
13use std::num::ParseIntError;
14use std::str::FromStr;
15
16use serde::de::Visitor;
17use serde::{Deserialize, Serialize};
18
19/// PCI has four interrupt pins A->D.
20#[derive(Copy, Clone)]
21pub enum PciInterruptPin {
22    IntA,
23    IntB,
24    IntC,
25    IntD,
26}
27
28impl PciInterruptPin {
29    pub fn to_mask(self) -> u32 {
30        self as u32
31    }
32}
33
34#[derive(Clone, Copy, PartialEq, Eq, PartialOrd)]
35pub struct PciBdf(u32);
36
37struct PciBdfVisitor;
38
39impl Visitor<'_> for PciBdfVisitor {
40    type Value = PciBdf;
41
42    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
43        formatter.write_str("struct PciBdf")
44    }
45
46    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
47    where
48        E: serde::de::Error,
49    {
50        PciBdf::from_str(v).map_err(serde::de::Error::custom)
51    }
52}
53
54impl<'de> serde::Deserialize<'de> for PciBdf {
55    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56    where
57        D: serde::Deserializer<'de>,
58    {
59        deserializer.deserialize_str(PciBdfVisitor)
60    }
61}
62
63impl serde::Serialize for PciBdf {
64    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65    where
66        S: serde::Serializer,
67    {
68        serializer.collect_str(&self.to_string())
69    }
70}
71
72impl PciBdf {
73    pub fn segment(&self) -> u16 {
74        ((self.0 >> 16) & 0xffff) as u16
75    }
76
77    pub fn bus(&self) -> u8 {
78        ((self.0 >> 8) & 0xff) as u8
79    }
80
81    pub fn device(&self) -> u8 {
82        ((self.0 >> 3) & 0x1f) as u8
83    }
84
85    pub fn function(&self) -> u8 {
86        (self.0 & 0x7) as u8
87    }
88
89    pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> Self {
90        Self(
91            ((segment as u32) << 16)
92                | ((bus as u32) << 8)
93                | (((device & 0x1f) as u32) << 3)
94                | (function & 0x7) as u32,
95        )
96    }
97}
98
99impl From<u32> for PciBdf {
100    fn from(bdf: u32) -> Self {
101        Self(bdf)
102    }
103}
104
105impl From<PciBdf> for u32 {
106    fn from(bdf: PciBdf) -> Self {
107        bdf.0
108    }
109}
110
111impl From<&PciBdf> for u32 {
112    fn from(bdf: &PciBdf) -> Self {
113        bdf.0
114    }
115}
116
117impl From<PciBdf> for u16 {
118    fn from(bdf: PciBdf) -> Self {
119        (bdf.0 & 0xffff) as u16
120    }
121}
122
123impl From<&PciBdf> for u16 {
124    fn from(bdf: &PciBdf) -> Self {
125        (bdf.0 & 0xffff) as u16
126    }
127}
128
129impl Debug for PciBdf {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        write!(
132            f,
133            "{:04x}:{:02x}:{:02x}.{:01x}",
134            self.segment(),
135            self.bus(),
136            self.device(),
137            self.function()
138        )
139    }
140}
141
142impl Display for PciBdf {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        write!(
145            f,
146            "{:04x}:{:02x}:{:02x}.{:01x}",
147            self.segment(),
148            self.bus(),
149            self.device(),
150            self.function()
151        )
152    }
153}
154
155/// Errors associated with parsing a BDF string.
156#[derive(Debug, thiserror::Error, displaydoc::Display)]
157pub enum PciBdfParseError {
158    /// Unable to parse bus/device/function number hex: {0}
159    InvalidHex(#[from] ParseIntError),
160    /// Invalid format: {0} (expected format: 0000:00:00.0)
161    InvalidFormat(String),
162}
163
164impl FromStr for PciBdf {
165    type Err = PciBdfParseError;
166
167    fn from_str(s: &str) -> Result<Self, Self::Err> {
168        let items: Vec<&str> = s.split('.').collect();
169        if items.len() != 2 {
170            return Err(PciBdfParseError::InvalidFormat(s.to_string()));
171        }
172        let function = u8::from_str_radix(items[1], 16)?;
173        let items: Vec<&str> = items[0].split(':').collect();
174        if items.len() != 3 {
175            return Err(PciBdfParseError::InvalidFormat(s.to_string()));
176        }
177        let segment = u16::from_str_radix(items[0], 16)?;
178        let bus = u8::from_str_radix(items[1], 16)?;
179        let device = u8::from_str_radix(items[2], 16)?;
180        Ok(PciBdf::new(segment, bus, device, function))
181    }
182}
183
184/// Represents the types of PCI headers allowed in the configuration registers.
185#[derive(Debug, Copy, Clone, Eq, PartialEq)]
186pub enum PciHeaderType {
187    Device,
188    Bridge,
189}
190
191/// Classes of PCI nodes.
192#[allow(dead_code)]
193#[derive(Copy, Clone)]
194pub enum PciClassCode {
195    TooOld,
196    MassStorage,
197    NetworkController,
198    DisplayController,
199    MultimediaController,
200    MemoryController,
201    BridgeDevice,
202    SimpleCommunicationController,
203    BaseSystemPeripheral,
204    InputDevice,
205    DockingStation,
206    Processor,
207    SerialBusController,
208    WirelessController,
209    IntelligentIoController,
210    EncryptionController,
211    DataAcquisitionSignalProcessing,
212    Other = 0xff,
213}
214
215impl PciClassCode {
216    pub fn get_register_value(self) -> u8 {
217        self as u8
218    }
219}
220
221/// A PCI subclass. Each class in `PciClassCode` can specify a unique set of subclasses. This trait
222/// is implemented by each subclass. It allows use of a trait object to generate configurations.
223pub trait PciSubclass {
224    /// Convert this subclass to the value used in the PCI specification.
225    fn get_register_value(&self) -> u8;
226}
227
228/// Subclasses of the MultimediaController class.
229#[allow(dead_code)]
230#[derive(Copy, Clone)]
231pub enum PciMultimediaSubclass {
232    VideoController = 0x00,
233    AudioController = 0x01,
234    TelephonyDevice = 0x02,
235    AudioDevice = 0x03,
236    Other = 0x80,
237}
238
239impl PciSubclass for PciMultimediaSubclass {
240    fn get_register_value(&self) -> u8 {
241        *self as u8
242    }
243}
244
245/// Subclasses of the BridgeDevice
246#[allow(dead_code)]
247#[derive(Copy, Clone)]
248pub enum PciBridgeSubclass {
249    HostBridge = 0x00,
250    IsaBridge = 0x01,
251    EisaBridge = 0x02,
252    McaBridge = 0x03,
253    PciToPciBridge = 0x04,
254    PcmciaBridge = 0x05,
255    NuBusBridge = 0x06,
256    CardBusBridge = 0x07,
257    RacEwayBridge = 0x08,
258    PciToPciSemiTransparentBridge = 0x09,
259    InfiniBrandToPciHostBridge = 0x0a,
260    OtherBridgeDevice = 0x80,
261}
262
263impl PciSubclass for PciBridgeSubclass {
264    fn get_register_value(&self) -> u8 {
265        *self as u8
266    }
267}
268
269/// Subclass of the SerialBus
270#[allow(dead_code)]
271#[derive(Copy, Clone)]
272pub enum PciSerialBusSubClass {
273    Firewire = 0x00,
274    Accessbus = 0x01,
275    Ssa = 0x02,
276    Usb = 0x03,
277}
278
279impl PciSubclass for PciSerialBusSubClass {
280    fn get_register_value(&self) -> u8 {
281        *self as u8
282    }
283}
284
285/// Mass Storage Sub Classes
286#[allow(dead_code)]
287#[derive(Copy, Clone)]
288pub enum PciMassStorageSubclass {
289    ScsiStorage = 0x00,
290    IdeInterface = 0x01,
291    FloppyController = 0x02,
292    IpiController = 0x03,
293    RaidController = 0x04,
294    AtaController = 0x05,
295    SataController = 0x06,
296    SerialScsiController = 0x07,
297    NvmController = 0x08,
298    MassStorage = 0x80,
299}
300
301impl PciSubclass for PciMassStorageSubclass {
302    fn get_register_value(&self) -> u8 {
303        *self as u8
304    }
305}
306
307/// Network Controller Sub Classes
308#[allow(dead_code)]
309#[derive(Copy, Clone)]
310pub enum PciNetworkControllerSubclass {
311    EthernetController = 0x00,
312    TokenRingController = 0x01,
313    FddiController = 0x02,
314    AtmController = 0x03,
315    IsdnController = 0x04,
316    WorldFipController = 0x05,
317    PicmgController = 0x06,
318    InfinibandController = 0x07,
319    FabricController = 0x08,
320    NetworkController = 0x80,
321}
322
323impl PciSubclass for PciNetworkControllerSubclass {
324    fn get_register_value(&self) -> u8 {
325        *self as u8
326    }
327}
328
329/// Types of PCI capabilities.
330#[derive(Debug, PartialEq, Eq, Copy, Clone)]
331#[allow(dead_code)]
332#[allow(non_camel_case_types)]
333#[repr(u8)]
334pub enum PciCapabilityId {
335    ListId = 0,
336    PowerManagement = 0x01,
337    AcceleratedGraphicsPort = 0x02,
338    VitalProductData = 0x03,
339    SlotIdentification = 0x04,
340    MessageSignalledInterrupts = 0x05,
341    CompactPciHotSwap = 0x06,
342    PciX = 0x07,
343    HyperTransport = 0x08,
344    VendorSpecific = 0x09,
345    Debugport = 0x0A,
346    CompactPciCentralResourceControl = 0x0B,
347    PciStandardHotPlugController = 0x0C,
348    BridgeSubsystemVendorDeviceId = 0x0D,
349    AgpTargetPciPcibridge = 0x0E,
350    SecureDevice = 0x0F,
351    PciExpress = 0x10,
352    MsiX = 0x11,
353    SataDataIndexConf = 0x12,
354    PciAdvancedFeatures = 0x13,
355    PciEnhancedAllocation = 0x14,
356}
357
358impl From<u8> for PciCapabilityId {
359    fn from(c: u8) -> Self {
360        match c {
361            0 => PciCapabilityId::ListId,
362            0x01 => PciCapabilityId::PowerManagement,
363            0x02 => PciCapabilityId::AcceleratedGraphicsPort,
364            0x03 => PciCapabilityId::VitalProductData,
365            0x04 => PciCapabilityId::SlotIdentification,
366            0x05 => PciCapabilityId::MessageSignalledInterrupts,
367            0x06 => PciCapabilityId::CompactPciHotSwap,
368            0x07 => PciCapabilityId::PciX,
369            0x08 => PciCapabilityId::HyperTransport,
370            0x09 => PciCapabilityId::VendorSpecific,
371            0x0A => PciCapabilityId::Debugport,
372            0x0B => PciCapabilityId::CompactPciCentralResourceControl,
373            0x0C => PciCapabilityId::PciStandardHotPlugController,
374            0x0D => PciCapabilityId::BridgeSubsystemVendorDeviceId,
375            0x0E => PciCapabilityId::AgpTargetPciPcibridge,
376            0x0F => PciCapabilityId::SecureDevice,
377            0x10 => PciCapabilityId::PciExpress,
378            0x11 => PciCapabilityId::MsiX,
379            0x12 => PciCapabilityId::SataDataIndexConf,
380            0x13 => PciCapabilityId::PciAdvancedFeatures,
381            0x14 => PciCapabilityId::PciEnhancedAllocation,
382            _ => PciCapabilityId::ListId,
383        }
384    }
385}
386
387/// Types of PCI Express capabilities.
388#[derive(PartialEq, Eq, Copy, Clone, Debug)]
389#[allow(dead_code)]
390#[repr(u16)]
391pub enum PciExpressCapabilityId {
392    NullCapability = 0x0000,
393    AdvancedErrorReporting = 0x0001,
394    VirtualChannelMultiFunctionVirtualChannelNotPresent = 0x0002,
395    DeviceSerialNumber = 0x0003,
396    PowerBudgeting = 0x0004,
397    RootComplexLinkDeclaration = 0x0005,
398    RootComplexInternalLinkControl = 0x0006,
399    RootComplexEventCollectorEndpointAssociation = 0x0007,
400    MultiFunctionVirtualChannel = 0x0008,
401    VirtualChannelMultiFunctionVirtualChannelPresent = 0x0009,
402    RootComplexRegisterBlock = 0x000a,
403    VendorSpecificExtendedCapability = 0x000b,
404    ConfigurationAccessCorrelation = 0x000c,
405    AccessControlServices = 0x000d,
406    AlternativeRoutingIdentificationInterpretation = 0x000e,
407    AddressTranslationServices = 0x000f,
408    SingleRootIoVirtualization = 0x0010,
409    DeprecatedMultiRootIoVirtualization = 0x0011,
410    Multicast = 0x0012,
411    PageRequestInterface = 0x0013,
412    ReservedForAmd = 0x0014,
413    ResizeableBar = 0x0015,
414    DynamicPowerAllocation = 0x0016,
415    ThpRequester = 0x0017,
416    LatencyToleranceReporting = 0x0018,
417    SecondaryPciExpress = 0x0019,
418    ProtocolMultiplexing = 0x001a,
419    ProcessAddressSpaceId = 0x001b,
420    LnRequester = 0x001c,
421    DownstreamPortContainment = 0x001d,
422    L1PmSubstates = 0x001e,
423    PrecisionTimeMeasurement = 0x001f,
424    PciExpressOverMphy = 0x0020,
425    FRSQueueing = 0x0021,
426    ReadinessTimeReporting = 0x0022,
427    DesignatedVendorSpecificExtendedCapability = 0x0023,
428    VfResizeableBar = 0x0024,
429    DataLinkFeature = 0x0025,
430    PhysicalLayerSixteenGts = 0x0026,
431    LaneMarginingAtTheReceiver = 0x0027,
432    HierarchyId = 0x0028,
433    NativePcieEnclosureManagement = 0x0029,
434    PhysicalLayerThirtyTwoGts = 0x002a,
435    AlternateProtocol = 0x002b,
436    SystemFirmwareIntermediary = 0x002c,
437    ShadowFunctions = 0x002d,
438    DataObjectExchange = 0x002e,
439    Reserved = 0x002f,
440    ExtendedCapabilitiesAbsence = 0xffff,
441}
442
443impl From<u16> for PciExpressCapabilityId {
444    fn from(c: u16) -> Self {
445        match c {
446            0x0000 => PciExpressCapabilityId::NullCapability,
447            0x0001 => PciExpressCapabilityId::AdvancedErrorReporting,
448            0x0002 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelNotPresent,
449            0x0003 => PciExpressCapabilityId::DeviceSerialNumber,
450            0x0004 => PciExpressCapabilityId::PowerBudgeting,
451            0x0005 => PciExpressCapabilityId::RootComplexLinkDeclaration,
452            0x0006 => PciExpressCapabilityId::RootComplexInternalLinkControl,
453            0x0007 => PciExpressCapabilityId::RootComplexEventCollectorEndpointAssociation,
454            0x0008 => PciExpressCapabilityId::MultiFunctionVirtualChannel,
455            0x0009 => PciExpressCapabilityId::VirtualChannelMultiFunctionVirtualChannelPresent,
456            0x000a => PciExpressCapabilityId::RootComplexRegisterBlock,
457            0x000b => PciExpressCapabilityId::VendorSpecificExtendedCapability,
458            0x000c => PciExpressCapabilityId::ConfigurationAccessCorrelation,
459            0x000d => PciExpressCapabilityId::AccessControlServices,
460            0x000e => PciExpressCapabilityId::AlternativeRoutingIdentificationInterpretation,
461            0x000f => PciExpressCapabilityId::AddressTranslationServices,
462            0x0010 => PciExpressCapabilityId::SingleRootIoVirtualization,
463            0x0011 => PciExpressCapabilityId::DeprecatedMultiRootIoVirtualization,
464            0x0012 => PciExpressCapabilityId::Multicast,
465            0x0013 => PciExpressCapabilityId::PageRequestInterface,
466            0x0014 => PciExpressCapabilityId::ReservedForAmd,
467            0x0015 => PciExpressCapabilityId::ResizeableBar,
468            0x0016 => PciExpressCapabilityId::DynamicPowerAllocation,
469            0x0017 => PciExpressCapabilityId::ThpRequester,
470            0x0018 => PciExpressCapabilityId::LatencyToleranceReporting,
471            0x0019 => PciExpressCapabilityId::SecondaryPciExpress,
472            0x001a => PciExpressCapabilityId::ProtocolMultiplexing,
473            0x001b => PciExpressCapabilityId::ProcessAddressSpaceId,
474            0x001c => PciExpressCapabilityId::LnRequester,
475            0x001d => PciExpressCapabilityId::DownstreamPortContainment,
476            0x001e => PciExpressCapabilityId::L1PmSubstates,
477            0x001f => PciExpressCapabilityId::PrecisionTimeMeasurement,
478            0x0020 => PciExpressCapabilityId::PciExpressOverMphy,
479            0x0021 => PciExpressCapabilityId::FRSQueueing,
480            0x0022 => PciExpressCapabilityId::ReadinessTimeReporting,
481            0x0023 => PciExpressCapabilityId::DesignatedVendorSpecificExtendedCapability,
482            0x0024 => PciExpressCapabilityId::VfResizeableBar,
483            0x0025 => PciExpressCapabilityId::DataLinkFeature,
484            0x0026 => PciExpressCapabilityId::PhysicalLayerSixteenGts,
485            0x0027 => PciExpressCapabilityId::LaneMarginingAtTheReceiver,
486            0x0028 => PciExpressCapabilityId::HierarchyId,
487            0x0029 => PciExpressCapabilityId::NativePcieEnclosureManagement,
488            0x002a => PciExpressCapabilityId::PhysicalLayerThirtyTwoGts,
489            0x002b => PciExpressCapabilityId::AlternateProtocol,
490            0x002c => PciExpressCapabilityId::SystemFirmwareIntermediary,
491            0x002d => PciExpressCapabilityId::ShadowFunctions,
492            0x002e => PciExpressCapabilityId::DataObjectExchange,
493            0xffff => PciExpressCapabilityId::ExtendedCapabilitiesAbsence,
494            _ => PciExpressCapabilityId::Reserved,
495        }
496    }
497}
498
499/// See pci_regs.h in kernel
500#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
501pub enum PciBarRegionType {
502    Memory32BitRegion = 0,
503    IoRegion = 0x01,
504    Memory64BitRegion = 0x04,
505}
506
507#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
508pub enum PciBarPrefetchable {
509    NotPrefetchable = 0,
510    Prefetchable = 0x08,
511}
512
513impl From<PciBarPrefetchable> for bool {
514    fn from(val: PciBarPrefetchable) -> Self {
515        match val {
516            PciBarPrefetchable::NotPrefetchable => false,
517            PciBarPrefetchable::Prefetchable => true,
518        }
519    }
520}
521
522#[cfg(test)]
523mod tests {
524    use super::*;
525
526    #[test]
527    fn test_pci_bdf_new() {
528        let bdf = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
529        assert_eq!(bdf.segment(), 0x1234);
530        assert_eq!(bdf.bus(), 0x56);
531        assert_eq!(bdf.device(), 0x1f);
532        assert_eq!(bdf.function(), 0x7);
533    }
534
535    #[test]
536    fn test_pci_bdf_from_u32() {
537        let bdf = PciBdf::from(0x12345678);
538        assert_eq!(bdf.segment(), 0x1234);
539        assert_eq!(bdf.bus(), 0x56);
540        assert_eq!(bdf.device(), 0x0f);
541        assert_eq!(bdf.function(), 0x0);
542    }
543
544    #[test]
545    fn test_pci_bdf_to_u32() {
546        let bdf = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
547        let val: u32 = bdf.into();
548        assert_eq!(val, 0x123456ff);
549    }
550
551    #[test]
552    fn test_pci_bdf_to_u16() {
553        let bdf = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
554        let val: u16 = bdf.into();
555        assert_eq!(val, 0x56ff);
556    }
557
558    #[test]
559    fn test_pci_bdf_from_str_valid() {
560        let bdf = PciBdf::from_str("1234:56:1f.7").unwrap();
561        assert_eq!(bdf.segment(), 0x1234);
562        assert_eq!(bdf.bus(), 0x56);
563        assert_eq!(bdf.device(), 0x1f);
564        assert_eq!(bdf.function(), 0x7);
565    }
566
567    #[test]
568    fn test_pci_bdf_from_str_zero() {
569        let bdf = PciBdf::from_str("0000:00:00.0").unwrap();
570        assert_eq!(bdf.segment(), 0);
571        assert_eq!(bdf.bus(), 0);
572        assert_eq!(bdf.device(), 0);
573        assert_eq!(bdf.function(), 0);
574    }
575
576    #[test]
577    fn test_pci_bdf_from_str_invalid_format() {
578        assert!(matches!(
579            PciBdf::from_str("invalid"),
580            Err(PciBdfParseError::InvalidFormat(_))
581        ));
582        assert!(matches!(
583            PciBdf::from_str("1234:56"),
584            Err(PciBdfParseError::InvalidFormat(_))
585        ));
586        assert!(matches!(
587            PciBdf::from_str("1234:56:78:9a.b"),
588            Err(PciBdfParseError::InvalidFormat(_))
589        ));
590    }
591
592    #[test]
593    fn test_pci_bdf_from_str_invalid_hex() {
594        assert!(matches!(
595            PciBdf::from_str("xxxx:00:00.0"),
596            Err(PciBdfParseError::InvalidHex(_))
597        ));
598        assert!(matches!(
599            PciBdf::from_str("0000:xx:00.0"),
600            Err(PciBdfParseError::InvalidHex(_))
601        ));
602        assert!(matches!(
603            PciBdf::from_str("0000:00:xx.0"),
604            Err(PciBdfParseError::InvalidHex(_))
605        ));
606        assert!(matches!(
607            PciBdf::from_str("0000:00:00.x"),
608            Err(PciBdfParseError::InvalidHex(_))
609        ));
610    }
611
612    #[test]
613    fn test_pci_bdf_display() {
614        let bdf = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
615        assert_eq!(format!("{}", bdf), "1234:56:1f.7");
616    }
617
618    #[test]
619    fn test_pci_bdf_debug() {
620        let bdf = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
621        assert_eq!(format!("{:?}", bdf), "1234:56:1f.7");
622    }
623
624    #[test]
625    fn test_pci_bdf_partial_eq() {
626        let bdf1 = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
627        let bdf2 = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
628        let bdf3 = PciBdf::new(0x1234, 0x56, 0x1f, 0x6);
629        assert_eq!(bdf1, bdf2);
630        assert_ne!(bdf1, bdf3);
631    }
632
633    #[test]
634    fn test_pci_bdf_partial_ord() {
635        let bdf1 = PciBdf::new(0x1234, 0x56, 0x1f, 0x6);
636        let bdf2 = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
637        assert!(bdf1 < bdf2);
638    }
639
640    #[test]
641    fn test_pci_bdf_deserialize_ok() {
642        // Test deserializer
643        let visitor = PciBdfVisitor;
644        let result = visitor
645            .visit_str::<serde::de::value::Error>("1234:56:1f.7")
646            .unwrap();
647        assert_eq!(result, PciBdf::new(0x1234, 0x56, 0x1f, 0x7));
648    }
649
650    #[test]
651    fn test_pci_bdf_deserialize_invalid() {
652        // Test deserializer with invalid input returns error
653        let visitor = PciBdfVisitor;
654        assert!(visitor
655            .visit_str::<serde::de::value::Error>("invalid")
656            .is_err());
657    }
658
659    #[test]
660    fn test_pci_bdf_serialize() {
661        // Test serializer using serde_test
662        let bdf = PciBdf::new(0x1234, 0x56, 0x1f, 0x7);
663        serde_test::assert_tokens(&bdf, &[serde_test::Token::Str("1234:56:1f.7")]);
664    }
665}