nvme-telem 0.3.0

NVMe S.M.A.R.T. / telemetry collection for Linux
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
// src/nvme/types.rs

use nvme_cli_sys::{nvme_error_log_page, nvme_id_ctrl, nvme_smart_log};
use serde::Serialize;
use std::io;
use std::os::raw::c_char;

/// Result type for NVMe operations.
pub type Result<T> = io::Result<T>;

// =============================================================================
// CONTROLLER IDENTITY STRUCTS
// =============================================================================

/// Controller identity and basic information.
///
/// Contains fundamental identification data for the NVMe controller including
/// vendor information, serial numbers, firmware revision, and controller IDs.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlIdentity {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// PCI Vendor ID
    pub vid: u16,

    /// PCI Subsystem Vendor ID
    pub ssvid: u16,

    /// Serial Number (ASCII, space padded)
    pub serial_number: String,

    /// Model Number (ASCII, space padded)
    pub model_number: String,

    /// Firmware Revision (ASCII, space padded)
    pub firmware_rev: String,

    /// IEEE OUI Identifier (3 bytes)
    pub ieee_oui: [u8; 3],

    /// Controller ID
    pub cntlid: u16,

    /// NVMe specification version
    pub ver: u32,

    /// NVM Subsystem NQN (ASCII)
    pub subnqn: String,

    /// FRU GUID / Field Replaceable Unit GUID
    pub fguid: [u8; 16],

    /// Controller Type
    pub cntrltype: u8,
}

impl CtrlIdentity {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            vid: raw.vid,
            ssvid: raw.ssvid,
            serial_number: parse_ascii_field(&raw.sn),
            model_number: parse_ascii_field(&raw.mn),
            firmware_rev: parse_ascii_field(&raw.fr),
            ieee_oui: raw.ieee,
            cntlid: raw.cntlid,
            ver: raw.ver,
            subnqn: parse_ascii_field(&raw.subnqn),
            fguid: convert_cchar_to_u8_array_16(&raw.fguid),
            cntrltype: raw.cntrltype,
        }
    }
}

// =============================================================================
// CONTROLLER CAPACITY & STORAGE STRUCTS
// =============================================================================

/// Controller capacity and storage information.
///
/// Provides details about total NVM capacity, unallocated space, and maximum
/// endurance group capacity for the controller.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlCapacity {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Total NVM Capacity (bytes)
    pub total_nvm_bytes: u128,

    /// Unallocated NVM Capacity (bytes)
    pub unallocated_nvm_bytes: u128,

    /// Maximum Endurance Group Capacity (bytes)
    pub max_endurance_group_bytes: u128,

    /// Maximum Capacity of NVM Area
    pub max_nvm_area: u32,
}

impl CtrlCapacity {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            total_nvm_bytes: u128::from_le_bytes(raw.tnvmcap),
            unallocated_nvm_bytes: u128::from_le_bytes(raw.unvmcap),
            max_endurance_group_bytes: u128::from_le_bytes(raw.megcap),
            max_nvm_area: raw.maxcna,
        }
    }
}

// =============================================================================
// CONTROLLER CAPABILITIES & FEATURES STRUCTS
// =============================================================================

/// Controller capabilities and feature support.
///
/// Bitfield indicators for optional admin commands, NVM commands, log page
/// attributes, sanitize capabilities, and various controller features.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlCapabilities {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Optional Admin Command Support (bitfield)
    pub oacs: u16,

    /// Optional NVM Command Support (bitfield)
    pub oncs: u16,

    /// Log Page Attributes (bitfield)
    pub lpa: u8,

    /// Controller Attributes (bitfield)
    pub ctratt: u32,

    /// Optional Asynchronous Events Supported (bitfield)
    pub oaes: u32,

    /// Sanitize Capabilities (bitfield)
    pub sanicap: u32,

    /// SGL Support (bitfield)
    pub sgls: u32,

    /// Volatile Write Cache
    pub vwc: u8,

    /// Format NVM Attributes
    pub fna: u8,

    /// ANA Capabilities
    pub anacap: u8,

    /// ANA Transition Time
    pub anatt: u8,

    /// ANA Group Identifier Maximum
    pub anagrpmax: u32,

    /// Number of ANA Group Identifiers
    pub nanagrpid: u32,

    /// Fused Operation Support
    pub fuses: u16,

    /// Optional Copy Formats Supported
    pub ocfs: u16,

    /// Controller Multi-Path I/O and Namespace Sharing Capabilities
    pub cmic: u8,

    /// Replay Protected Memory Block Support
    pub rpmbs: u32,
}

impl CtrlCapabilities {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            oacs: raw.oacs,
            oncs: raw.oncs,
            lpa: raw.lpa,
            ctratt: raw.ctratt,
            oaes: raw.oaes,
            sanicap: raw.sanicap,
            sgls: raw.sgls,
            vwc: raw.vwc,
            fna: raw.fna,
            anacap: raw.anacap,
            anatt: raw.anatt,
            anagrpmax: raw.anagrpmax,
            nanagrpid: raw.nanagrpid,
            fuses: raw.fuses,
            ocfs: raw.ocfs,
            cmic: raw.cmic,
            rpmbs: raw.rpmbs,
        }
    }
}

// =============================================================================
// CONTROLLER LIMITS & CONSTRAINTS STRUCTS
// =============================================================================

/// Controller operational limits and constraints.
///
/// Defines maximum transfer sizes, queue entry sizes, outstanding commands,
/// namespace counts, and atomic operation units.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlLimits {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Maximum Data Transfer Size (power of 2)
    pub mdts: u8,

    /// Submission Queue Entry Size (encoded)
    pub sqes: u8,

    /// Completion Queue Entry Size (encoded)
    pub cqes: u8,

    /// Maximum Outstanding Commands
    pub maxcmd: u16,

    /// Number of Namespaces
    pub nn: u32,

    /// Maximum Number of Allowed Namespaces
    pub mnan: u32,

    /// Abort Command Limit
    pub acl: u8,

    /// Asynchronous Event Request Limit
    pub aerl: u8,

    /// Atomic Write Unit Normal (logical blocks)
    pub awun: u16,

    /// Atomic Write Unit Power Fail (logical blocks)
    pub awupf: u16,

    /// Atomic Compare & Write Unit (logical blocks)
    pub acwu: u16,

    /// NVM Set Identifier Maximum
    pub nsetidmax: u16,

    /// Endurance Group Identifier Maximum
    pub endgidmax: u16,
}

impl CtrlLimits {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            mdts: raw.mdts,
            sqes: raw.sqes,
            cqes: raw.cqes,
            maxcmd: raw.maxcmd,
            nn: raw.nn,
            mnan: raw.mnan,
            acl: raw.acl,
            aerl: raw.aerl,
            awun: raw.awun,
            awupf: raw.awupf,
            acwu: raw.acwu,
            nsetidmax: raw.nsetidmax,
            endgidmax: raw.endgidmax,
        }
    }
}

// =============================================================================
// CONTROLLER THERMAL MANAGEMENT STRUCTS
// =============================================================================

/// Controller thermal management configuration.
///
/// Temperature thresholds and thermal management settings for the controller.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlThermals {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Warning Composite Temperature Threshold (Kelvin)
    pub wctemp_k: u16,

    /// Critical Composite Temperature Threshold (Kelvin)
    pub cctemp_k: u16,

    /// Minimum Thermal Management Temperature (Kelvin)
    pub mntmt_k: u16,

    /// Maximum Thermal Management Temperature (Kelvin)
    pub mxtmt_k: u16,

    /// Host Controlled Thermal Management Attributes
    pub hctma: u16,
}

impl CtrlThermals {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            wctemp_k: raw.wctemp,
            cctemp_k: raw.cctemp,
            mntmt_k: raw.mntmt,
            mxtmt_k: raw.mxtmt,
            hctma: raw.hctma,
        }
    }
}

// =============================================================================
// CONTROLLER FIRMWARE STRUCTS
// =============================================================================

/// Controller firmware update configuration.
///
/// Settings related to firmware updates including update capabilities,
/// granularity, and activation timing.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlFirmware {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Firmware Updates (bitfield)
    pub frmw: u8,

    /// Firmware Update Granularity (4KB units, 0 = no limit)
    pub fwug: u8,

    /// Maximum Time for Firmware Activation (100ms units)
    pub mtfa: u16,
}

impl CtrlFirmware {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            frmw: raw.frmw,
            fwug: raw.fwug,
            mtfa: raw.mtfa,
        }
    }
}

// =============================================================================
// CONTROLLER POWER MANAGEMENT STRUCTS
// =============================================================================

/// Controller power state descriptors.
///
/// Contains all 32 power state descriptors defined by the NVMe specification
/// along with the number of supported states and autonomous transition attributes.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlPowerStates {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Number of Power States Supported (npss + 1)
    pub num_power_states: u8,

    /// Autonomous Power State Transition Attributes
    pub apsta: u8,
    // TODO - figure out the rust syntax here :)
    // Power State Descriptors (all 32 entries from spec)
    //pub power_state_descriptors: [nvme_id_power_state; 32],
}

impl CtrlPowerStates {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            num_power_states: raw.npss,
            apsta: raw.apsta,
            //power_state_descriptors: raw.psd,
        }
    }
}

// =============================================================================
// CONTROLLER HOST MEMORY BUFFER STRUCTS
// =============================================================================

/// Controller host memory buffer configuration.
///
/// Settings for the Host Memory Buffer (HMB) feature, including preferred and
/// minimum sizes, and descriptor limits.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlHostMemory {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Host Memory Buffer Preferred Size (4KB units)
    pub hmpre: u32,

    /// Host Memory Buffer Minimum Size (4KB units)
    pub hmmin: u32,

    /// Host Memory Buffer Minimum Descriptor Entry Size (4KB units)
    pub hmminds: u32,

    /// Host Memory Buffer Maximum Descriptor Entries
    pub hmmaxd: u16,
}

impl CtrlHostMemory {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            hmpre: raw.hmpre,
            hmmin: raw.hmmin,
            hmminds: raw.hmminds,
            hmmaxd: raw.hmmaxd,
        }
    }
}

// =============================================================================
// CONTROLLER ARBITRATION & QOS STRUCTS
// =============================================================================

/// Controller arbitration and quality of service settings.
///
/// Configuration for weighted round-robin arbitration.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlArbitration {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Recommended Arbitration Burst
    pub rab: u8,
}

impl CtrlArbitration {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            rab: raw.rab,
        }
    }
}

// =============================================================================
// CONTROLLER DIAGNOSTICS & SELF-TEST STRUCTS
// =============================================================================

/// Controller diagnostic and self-test capabilities.
///
/// Information about device self-test features, timing, and error log capacity.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlDiagnostics {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Extended Device Self-test Time (minutes)
    pub edstt: u16,

    /// Device Self-test Options (bitfield)
    pub dsto: u8,

    /// Error Log Page Entries (0-based, actual count is elpe + 1)
    pub elpe: u8,
}

impl CtrlDiagnostics {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            edstt: raw.edstt,
            dsto: raw.dsto,
            elpe: raw.elpe,
        }
    }
}

// =============================================================================
// CONTROLLER ADVANCED FEATURES & TIMING STRUCTS
// =============================================================================

/// Controller advanced features and timing.
///
/// RTD3 (Runtime D3) latencies, command retry delays, subsystem reporting,
/// and other advanced controller features.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlAdvanced {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// RTD3 Resume Latency (microseconds)
    pub rtd3r_us: u32,

    /// RTD3 Entry Latency (microseconds)
    pub rtd3e_us: u32,

    /// Read Recovery Levels Supported
    pub rrls: u16,

    /// Command Retry Delay Time 1 (100ms units)
    pub crdt1: u16,

    /// Command Retry Delay Time 2 (100ms units)
    pub crdt2: u16,

    /// Command Retry Delay Time 3 (100ms units)
    pub crdt3: u16,

    /// NVM Subsystem Report
    pub nvmsr: u8,

    /// VPD Write Cycle Information
    pub vwci: u8,

    /// Management Endpoint Capabilities
    pub mec: u8,

    /// Keep Alive Support (100ms units)
    pub kas: u16,

    /// Persistent Event Log Size (4KB units)
    pub pels: u32,

    /// Domain Identifier
    pub domainid: u16,
}

impl CtrlAdvanced {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            rtd3r_us: raw.rtd3r,
            rtd3e_us: raw.rtd3e,
            rrls: raw.rrls,
            crdt1: raw.crdt1,
            crdt2: raw.crdt2,
            crdt3: raw.crdt3,
            nvmsr: raw.nvmsr,
            vwci: raw.vwci,
            mec: raw.mec,
            kas: raw.kas,
            pels: raw.pels,
            domainid: raw.domainid,
        }
    }
}

// =============================================================================
// CONTROLLER COMMAND SET STRUCTS
// =============================================================================

/// Controller command set configuration.
///
/// Vendor-specific command support and namespace write protection capabilities.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlCommandSets {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Admin Vendor Specific Command Configuration
    pub avscc: u8,

    /// NVM Vendor Specific Command Configuration
    pub icsvscc: u8,

    /// Namespace Write Protection Capabilities
    pub nwpc: u8,
}

impl CtrlCommandSets {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            avscc: raw.avscc,
            icsvscc: raw.icsvscc,
            nwpc: raw.nwpc,
        }
    }
}

// =============================================================================
// CONTROLLER FABRIC (NVMe-oF) STRUCTS
// =============================================================================

/// Controller fabric (NVMe-oF) configuration.
///
/// Settings specific to NVMe over Fabrics including capsule sizes,
/// offsets, and fabric command support.
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlFabric {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// I/O Command Capsule Supported Size (16-byte units)
    pub ioccsz: u32,

    /// I/O Response Capsule Supported Size (16-byte units)
    pub iorcsz: u32,

    /// In Capsule Data Offset (16-byte units)
    pub icdoff: u16,

    /// Fabric Controller Attributes
    pub fcatt: u8,

    /// Management Service Data Block Descriptor
    pub msdbd: u8,

    /// Optional Fabric Commands Support
    pub ofcs: u16,
}

impl CtrlFabric {
    pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
        Self {
            nvme_name,
            ioccsz: raw.ioccsz,
            iorcsz: raw.iorcsz,
            icdoff: raw.icdoff,
            fcatt: raw.fcatt,
            msdbd: raw.msdbd,
            ofcs: raw.ofcs,
        }
    }
}

// =============================================================================
// LOG PAGE STRUCTS
// =============================================================================

/// NVMe S.M.A.R.T./Health Information Log (Log Page 0x02).
///
/// Provides S.M.A.R.T. and general health information over the life of the controller.
/// Data is retained across power cycles unless otherwise specified.
#[derive(Debug, Serialize, PartialEq)]
pub struct NvmeSmartLog {
    /// NVMe device name (e.g., "nvme0")
    pub nvme_name: String,

    /// Serial Number (ASCII, space padded)
    pub serial_number: String,

    /// Critical Warning bitmask (Byte 00):
    /// - Bit 0: Available spare below threshold
    /// - Bit 1: Temperature threshold condition
    /// - Bit 2: NVM subsystem degraded reliability
    /// - Bit 3: All media read-only
    /// - Bit 4: Volatile memory backup failed
    /// - Bit 5: Persistent memory region read-only
    /// - Bit 6: Indeterminate personality state
    /// - Bit 7: Reserved
    ///
    /// A value of 0 means no critical warnings
    pub critical_warning: u8,

    /// Composite Temperature (Bytes 02:01).
    ///
    /// Current temperature in Kelvins representing the composite temperature
    /// of the controller and associated namespaces.
    pub temperature: u16,

    /// Available Spare (Byte 03).
    ///
    /// Normalized percentage (0-100%) of remaining spare capacity available.
    pub avail_spare: u8,

    /// Available Spare Threshold (Byte 04).
    ///
    /// When Available Spare falls below this threshold, an asynchronous event may occur.
    /// Normalized percentage (0-100%).
    pub spare_thresh: u8,

    /// Percentage Used (Byte 05).
    ///
    /// Vendor specific estimate of the percentage of NVM subsystem life used.
    /// Value of 100 indicates estimated endurance has been consumed.
    /// May exceed 100. Updated once per power-on hour.
    pub percent_used: u8,

    /// Endurance Group Critical Warning Summary (Byte 06).
    ///
    /// - Bit 0: Endurance Group available spare capacity below threshold
    /// - Bit 1: Reserved
    /// - Bit 2: Endurance Group degraded reliability
    /// - Bit 3: Endurance Group read-only
    /// - Bits 4-7: Reserved
    pub endurance_grp_critical_warning_summary: u8,

    /// Data Units Read (Bytes 47:32).
    ///
    /// Number of 512-byte data units read from controller.
    /// Reported in thousands (value of 1 = 1,000 units).
    /// Does not include metadata.
    pub data_units_read: u128,

    /// Data Units Written (Bytes 63:48).
    ///
    /// Number of 512-byte data units written to controller.
    /// Reported in thousands (value of 1 = 1,000 units).
    /// Does not include metadata.
    pub data_units_written: u128,

    /// Host Read Commands (Bytes 79:64).
    ///
    /// Number of S.M.A.R.T. Host Read Commands completed by the controller.
    pub host_read_commands: u128,

    /// Host Write Commands (Bytes 95:80).
    ///
    /// Number of User Data Out Commands completed by the controller.
    pub host_write_commands: u128,

    /// Controller Busy Time (Bytes 111:96).
    ///
    /// Amount of time controller is busy with I/O commands.
    /// Reported in minutes.
    pub controller_busy_time: u128,

    /// Power Cycles (Bytes 127:112).
    ///
    /// Number of power cycles.
    pub power_cycles: u128,

    /// Power On Hours (Bytes 143:128).
    ///
    /// Number of power-on hours.
    /// May not include time controller was powered in non-operational state.
    pub power_on_hours: u128,

    /// Unsafe Shutdowns / Unexpected Power Losses (Bytes 159:144).
    ///
    /// Count of unexpected power losses where controller was not ready
    /// to be powered off or media was not in shutdown state.
    pub unsafe_shutdowns: u128,

    /// Media and Data Integrity Errors (Bytes 175:160).
    ///
    /// Number of occurrences where controller detected un-recovered data integrity error.
    /// Includes uncorrectable ECC, CRC checksum failure, LBA tag mismatch.
    pub media_errors: u128,

    /// Number of Error Information Log Entries (Bytes 191:176).
    ///
    /// Number of Error Information Log Entries over the life of the controller.
    pub num_err_log_entries: u128,

    /// Warning Composite Temperature Time (Bytes 195:192).
    ///
    /// Time in minutes that Composite Temperature is >= Warning Threshold
    /// and < Critical Threshold.
    pub warning_temp_time: u32,

    /// Critical Composite Temperature Time (Bytes 199:196).
    ///
    /// Time in minutes that Composite Temperature is >= Critical Threshold.
    pub critical_comp_time: u32,

    /// Temperature Sensor 1 (Bytes 201:200).
    ///
    /// Current temperature reported by temperature sensor 1 in Kelvins.
    pub temperature_sensor_1: Option<u16>,

    /// Temperature Sensor 2 (Bytes 203:202).
    ///
    /// Current temperature reported by temperature sensor 2 in Kelvins.
    pub temperature_sensor_2: Option<u16>,

    /// Temperature Sensor 3 (Bytes 205:204).
    ///
    /// Current temperature reported by temperature sensor 3 in Kelvins.
    pub temperature_sensor_3: Option<u16>,

    /// Temperature Sensor 4 (Bytes 207:206).
    ///
    /// Current temperature reported by temperature sensor 4 in Kelvins.
    pub temperature_sensor_4: Option<u16>,

    /// Temperature Sensor 5 (Bytes 209:208).
    ///
    /// Current temperature reported by temperature sensor 5 in Kelvins.
    pub temperature_sensor_5: Option<u16>,

    /// Temperature Sensor 6 (Bytes 211:210).
    ///
    /// Current temperature reported by temperature sensor 6 in Kelvins.
    pub temperature_sensor_6: Option<u16>,

    /// Temperature Sensor 7 (Bytes 213:212).
    ///
    /// Current temperature reported by temperature sensor 7 in Kelvins.
    pub temperature_sensor_7: Option<u16>,

    /// Temperature Sensor 8 (Bytes 215:214).
    ///
    /// Current temperature reported by temperature sensor 8 in Kelvins.
    pub temperature_sensor_8: Option<u16>,

    /// Thermal Management Temperature 1 Transition Count (Bytes 219:216).
    ///
    /// Number of times controller transitioned to lower power states to reduce
    /// temperature after rising above Thermal Management Temperature 1.
    /// Does not wrap after reaching 0xFFFFFFFF.
    pub thm_temp1_trans_count: u32,

    /// Thermal Management Temperature 2 Transition Count (Bytes 223:220).
    ///
    /// Number of times controller performed heavy thermal throttling to reduce
    /// temperature after rising above Thermal Management Temperature 2.
    /// Does not wrap after reaching 0xFFFFFFFF.
    pub thm_temp2_trans_count: u32,

    /// Total Time For Thermal Management Temperature 1 (Bytes 227:224).
    ///
    /// Number of seconds controller spent in lower power states due to
    /// Thermal Management Temperature 1. Reported in seconds.
    /// Does not wrap after reaching 0xFFFFFFFF.
    pub thm_temp1_total_time: u32,

    /// Total Time For Thermal Management Temperature 2 (Bytes 231:228).
    ///
    /// Number of seconds controller spent performing heavy throttling due to
    /// Thermal Management Temperature 2. Reported in seconds.
    /// Does not wrap after reaching 0xFFFFFFFF.
    pub thm_temp2_total_time: u32,
}

impl NvmeSmartLog {
    pub fn new(nvme_name: String, serial_number: String, raw: &nvme_smart_log) -> Self {
        Self {
            nvme_name,
            serial_number,

            critical_warning: raw.critical_warning,
            temperature: u16::from_le_bytes([raw.temperature[0], raw.temperature[1]]),
            avail_spare: raw.avail_spare,
            spare_thresh: raw.spare_thresh,
            percent_used: raw.percent_used,
            endurance_grp_critical_warning_summary: raw.endu_grp_crit_warn_sumry,

            data_units_read: u128::from_le_bytes(raw.data_units_read),
            data_units_written: u128::from_le_bytes(raw.data_units_written),
            host_read_commands: u128::from_le_bytes(raw.host_reads),
            host_write_commands: u128::from_le_bytes(raw.host_writes),
            controller_busy_time: u128::from_le_bytes(raw.ctrl_busy_time),
            power_cycles: u128::from_le_bytes(raw.power_cycles),
            power_on_hours: u128::from_le_bytes(raw.power_on_hours),
            unsafe_shutdowns: u128::from_le_bytes(raw.unsafe_shutdowns),
            media_errors: u128::from_le_bytes(raw.media_errors),
            num_err_log_entries: u128::from_le_bytes(raw.num_err_log_entries),

            warning_temp_time: raw.warning_temp_time,
            critical_comp_time: raw.critical_comp_time,

            temperature_sensor_1: match raw.temp_sensor[0] {
                0 => None,
                v => Some(v),
            },
            temperature_sensor_2: match raw.temp_sensor[1] {
                0 => None,
                v => Some(v),
            },
            temperature_sensor_3: match raw.temp_sensor[2] {
                0 => None,
                v => Some(v),
            },
            temperature_sensor_4: match raw.temp_sensor[3] {
                0 => None,
                v => Some(v),
            },
            temperature_sensor_5: match raw.temp_sensor[4] {
                0 => None,
                v => Some(v),
            },
            temperature_sensor_6: match raw.temp_sensor[5] {
                0 => None,
                v => Some(v),
            },
            temperature_sensor_7: match raw.temp_sensor[6] {
                0 => None,
                v => Some(v),
            },
            temperature_sensor_8: match raw.temp_sensor[7] {
                0 => None,
                v => Some(v),
            },

            thm_temp1_trans_count: raw.thm_temp1_trans_count,
            thm_temp2_trans_count: raw.thm_temp2_trans_count,
            thm_temp1_total_time: raw.thm_temp1_total_time,
            thm_temp2_total_time: raw.thm_temp2_total_time,
        }
    }
}

/// Error Information Log Entry (Log Identifier 0x01).
///
/// Contains detailed information about a specific error event that occurred
/// on the NVMe controller. Error entries are stored in a circular buffer,
/// with the oldest entries being overwritten when the buffer is full.
#[derive(Debug, Serialize, PartialEq)]
pub struct ErrorEntry {
    /// Error Count
    pub error_count: u64,

    /// Submission Queue ID
    pub submission_queue_id: u16,

    /// Command ID
    pub command_id: u16,

    /// Status Field
    pub status_field: u16,

    /// Parameter Error Location
    pub parameter_error_location: u16,

    /// LBA
    pub lba: u64,

    /// Namespace ID
    pub namespace_id: u32,

    /// Vendor Specific Information Available
    pub vendor_specific: u8,

    /// Transport Type
    pub transport_type: u8,

    /// Command Specific Information
    pub command_specific: u64,

    /// Transport Type Specific Information
    pub transport_type_specific_info: u16,
}

impl ErrorEntry {
    pub fn new(raw: &nvme_error_log_page) -> Self {
        Self {
            error_count: u64::from_le(raw.error_count),
            submission_queue_id: u16::from_le(raw.sqid),
            command_id: u16::from_le(raw.cmdid),
            status_field: u16::from_le(raw.status_field),
            parameter_error_location: u16::from_le(raw.parm_error_location),
            lba: u64::from_le(raw.lba),
            namespace_id: u32::from_le(raw.nsid),
            vendor_specific: raw.vs,
            transport_type: raw.trtype,
            command_specific: u64::from_le(raw.cs),
            transport_type_specific_info: u16::from_le(raw.trtype_spec_info),
        }
    }
}

/// Error Information Log (Log Page 0x01).
#[derive(Debug, Serialize)]
pub struct NvmeErrorLog {
    pub nvme_name: String,
    pub serial_number: String,
    pub entries: Vec<ErrorEntry>,
}

impl NvmeErrorLog {
    pub fn new(
        nvme_name: String,
        serial_number: String,
        raw_entries: Vec<nvme_error_log_page>,
    ) -> Self {
        let entries = raw_entries
            .iter()
            .filter(|e| e.error_count != 0)
            .map(ErrorEntry::new)
            .collect();

        Self {
            nvme_name,
            serial_number,
            entries,
        }
    }
}

/// Parse ASCII field from raw c_char byte array, trimming spaces and nulls.
///
/// NVMe spec uses C char arrays for ASCII fields. This function converts
/// to unsigned bytes, then parses as UTF-8 and trims whitespace/nulls.
pub fn parse_ascii_field(bytes: &[c_char]) -> String {
    let unsigned: Vec<u8> = bytes.iter().map(|&b| b as u8).collect();

    String::from_utf8_lossy(&unsigned)
        .trim_end_matches('\0')
        .trim()
        .to_string()
}

/// Convert [c_char; N] to [u8; N] for GUID fields.
pub fn convert_cchar_to_u8_array_16(bytes: &[c_char; 16]) -> [u8; 16] {
    let mut result = [0u8; 16];
    for (i, &b) in bytes.iter().enumerate() {
        result[i] = b as u8;
    }
    result
}