lsm/
local_disk.rs

1// Copyright (C) 2017-2025 Red Hat, Inc.
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26//
27// Author: Tony Asleson <tasleson@redhat.com>
28
29#![allow(non_camel_case_types)]
30
31use super::error::*;
32
33use std::convert::TryFrom;
34use std::ffi::CStr;
35use std::ffi::CString;
36use std::os::raw::c_void;
37use std::os::raw::{c_char, c_int};
38
39// Types for opaque C types
40enum c_lsm_string_list {}
41enum c_lsm_error {}
42enum c_lsm_led_handle {}
43enum c_lsm_led_slot_itr {}
44enum c_lsm_led_slot {}
45
46/// Possible values of link type.
47#[repr(C)]
48#[derive(Debug, Copy, Clone, PartialEq, Eq)]
49pub enum LinkType {
50    /// No support
51    NoSupport = -2,
52    /// Unknown
53    Unknown = -1,
54    /// Fibre channel
55    Fc = 0,
56    /// Serial Storage Architecture, old IBM tech.
57    Ssa = 2,
58    /// Serial Bus Protocol, used by IEEE 1394
59    Sbp = 3,
60    /// SCSI RDMA Protocol
61    Srp = 4,
62    /// Internet Small Computer System Interface
63    Iscsi = 5,
64    /// Serial attached SCSI
65    Sas = 6,
66    /// Automation/Drive Interface Transport Protocol, often used by Tape.
67    Adt = 7,
68    /// PATA/IDE or SATA.
69    Ata = 8,
70    // USB - Universal Serial Bus
71    Usb = 9,
72    /// SCSI over PCI-e
73    Sop = 10,
74    /// PCI-e, e.g. NVMe
75    Pcie = 11,
76}
77
78#[link(name = "storagemgmt")]
79extern "C" {
80
81    fn lsm_local_disk_list(
82        disk_paths: *mut *mut c_lsm_string_list,
83        lsm_error: *mut *mut c_lsm_error,
84    ) -> c_int;
85    fn lsm_string_list_free(lsm_string: *const c_lsm_string_list) -> c_int;
86    fn lsm_string_list_size(lsm_string_list: *const c_lsm_string_list) -> u32;
87    fn lsm_string_list_elem_get(
88        lsm_string_list: *const c_lsm_string_list,
89        index: u32,
90    ) -> *const c_char;
91
92    fn lsm_local_disk_vpd83_search(
93        vpd83: *const c_char,
94        lsm_string_list: *mut *mut c_lsm_string_list,
95        lsm_error: *mut *mut c_lsm_error,
96    ) -> c_int;
97
98    fn lsm_local_disk_serial_num_get(
99        disk_path: *const c_char,
100        serial_num: *mut *mut c_char,
101        lsm_error: *mut *mut c_lsm_error,
102    ) -> c_int;
103
104    fn lsm_local_disk_vpd83_get(
105        disk_path: *const c_char,
106        vpd83: *mut *mut c_char,
107        lsm_error: *mut *mut c_lsm_error,
108    ) -> c_int;
109
110    fn lsm_local_disk_health_status_get(
111        disk_path: *const c_char,
112        health_status: &mut i32,
113        lsm_error: *mut *mut c_lsm_error,
114    ) -> c_int;
115
116    fn lsm_local_disk_rpm_get(
117        disk_path: *const c_char,
118        rpm: &mut i32,
119        lsm_error: *mut *mut c_lsm_error,
120    ) -> c_int;
121
122    fn lsm_local_disk_link_type_get(
123        disk_path: *const c_char,
124        link_type: *mut LinkType,
125        lsm_error: *mut *mut c_lsm_error,
126    ) -> c_int;
127
128    fn lsm_local_disk_ident_led_on(
129        disk_path: *const c_char,
130        lsm_error: *mut *mut c_lsm_error,
131    ) -> c_int;
132
133    fn lsm_local_disk_ident_led_off(
134        disk_path: *const c_char,
135        lsm_error: *mut *mut c_lsm_error,
136    ) -> c_int;
137
138    fn lsm_local_disk_fault_led_on(
139        disk_path: *const c_char,
140        lsm_error: *mut *mut c_lsm_error,
141    ) -> c_int;
142
143    fn lsm_local_disk_fault_led_off(
144        disk_path: *const c_char,
145        lsm_error: *mut *mut c_lsm_error,
146    ) -> c_int;
147
148    fn lsm_local_disk_led_status_get(
149        disk_path: *const c_char,
150        led_status: &mut u32,
151        lsm_error: *mut *mut c_lsm_error,
152    ) -> c_int;
153
154    fn lsm_local_disk_link_speed_get(
155        disk_path: *const c_char,
156        link_speed: &mut u32,
157        lsm_error: *mut *mut c_lsm_error,
158    ) -> c_int;
159
160    fn lsm_error_number_get(lsm_error: *const c_lsm_error) -> c_int;
161    fn lsm_error_message_get(lsm_error: *const c_lsm_error) -> *const c_char;
162    fn lsm_error_free(lsm_error: *const c_lsm_error) -> c_int;
163
164    fn lsm_led_handle_get(handle: *mut *mut c_lsm_led_handle, flags: u64) -> c_int;
165    fn lsm_led_handle_free(handle: *const c_lsm_led_handle);
166    fn lsm_led_slot_iterator_free(
167        handle: *const c_lsm_led_handle,
168        slot_itr: *const c_lsm_led_slot_itr,
169    );
170    fn lsm_led_slot_iterator_get(
171        handle: *const c_lsm_led_handle,
172        slot_itr: *mut *mut c_lsm_led_slot_itr,
173        lsm_error: *mut *mut c_lsm_error,
174        flags: u64,
175    ) -> c_int;
176
177    fn lsm_led_slot_iterator_reset(
178        handle: *const c_lsm_led_handle,
179        slot_itr: *const c_lsm_led_slot_itr,
180    );
181
182    fn lsm_led_slot_next(
183        handle: *const c_lsm_led_handle,
184        itr: *mut c_lsm_led_slot_itr,
185    ) -> *const c_lsm_led_slot;
186
187    fn lsm_led_slot_status_get(slot: *const c_lsm_led_slot) -> u32;
188
189    fn lsm_led_slot_status_set(
190        handle: *const c_lsm_led_handle,
191        slot: *const c_lsm_led_slot,
192        status: u32,
193        lsm_error: *mut *mut c_lsm_error,
194        flag: u64,
195    ) -> c_int;
196
197    fn lsm_led_slot_id(slot: *const c_lsm_led_slot) -> *const c_char;
198    fn lsm_led_slot_device(slot: *const c_lsm_led_slot) -> *const c_char;
199}
200
201extern "C" {
202    fn free(ptr: *mut c_void);
203}
204
205fn c_char_ptr_to_string(c_str: *const c_char) -> String {
206    let m_str;
207    unsafe {
208        m_str = CStr::from_ptr(c_str);
209    }
210    let str_slice = m_str.to_str().expect("Invalid UTF-8");
211    str_slice.to_owned()
212}
213
214fn lsm_c_error_to_rust(lsm_error: *const c_lsm_error) -> LsmError {
215    let rc;
216    unsafe {
217        if !lsm_error.is_null() {
218            let code = lsm_error_number_get(lsm_error);
219            if code != -1 {
220                let message = lsm_error_message_get(lsm_error);
221
222                if !message.is_null() {
223                    rc = lsm_error_code_to_lsm_error(code, c_char_ptr_to_string(message))
224                } else {
225                    rc = lsm_error_code_to_lsm_error(
226                        code,
227                        String::from("no additional information provided"),
228                    )
229                }
230
231                // We had a valid pointer, so release the memory and expect success!
232                assert!(lsm_error_free(lsm_error) == 0);
233            } else {
234                panic!("Invalid lsm_error pointer used for error informational retrieval!");
235            }
236        } else {
237            rc = LsmError::LibBug(String::from("The C lsm error ptr was NULL"));
238        }
239    }
240    rc
241}
242
243fn c_lsm_string_list_to_vec(c_string_list: *const c_lsm_string_list) -> Vec<String> {
244    let mut result_list = Vec::new();
245
246    if !c_string_list.is_null() {
247        unsafe {
248            let num_items = lsm_string_list_size(c_string_list);
249
250            for i in 0..num_items {
251                let disk_path = CStr::from_ptr(lsm_string_list_elem_get(c_string_list, i));
252                result_list.push(disk_path.to_string_lossy().into_owned());
253            }
254            assert!(lsm_string_list_free(c_string_list) == 0);
255        }
256    }
257    result_list
258}
259
260/// Searches disk by supplied SCSI VPD 0x83 page NAA type ID.
261///
262/// Search all the disk paths of specified SCSI VPD 0x83 page NAA type ID.
263/// For any ATA and other non-SCSI protocol disks supporting VPD 0x83 pages
264/// NAA ID, their disk path will also be included.
265///
266/// Note: There maybe more than one disk path for any specified disk.
267pub fn vpd83_search(vpd83: &str) -> Result<Vec<String>> {
268    let c_search_string = CString::new(vpd83).expect("CString::new failed");
269    let mut disk_paths = std::ptr::null_mut();
270    let mut lsm_error = std::ptr::null_mut();
271
272    unsafe {
273        let rc =
274            lsm_local_disk_vpd83_search(c_search_string.as_ptr(), &mut disk_paths, &mut lsm_error);
275        if rc == 0 {
276            Ok(c_lsm_string_list_to_vec(disk_paths))
277        } else {
278            Err(lsm_c_error_to_rust(lsm_error))
279        }
280    }
281}
282
283/// Query serial number of specified disk path
284///
285/// Query the serial number of specified disk path.
286/// For SCSI/SAS/SATA/ATA disks, it will be extracted from SCSI VPD 0x80 page.
287pub fn serial_num_get(disk_path: &str) -> Result<String> {
288    let mut serial_num = std::ptr::null_mut();
289    let mut lsm_error = std::ptr::null_mut();
290
291    unsafe {
292        let c_sn = CString::new(disk_path).expect("CString::new failed");
293        let rc = lsm_local_disk_serial_num_get(c_sn.as_ptr(), &mut serial_num, &mut lsm_error);
294
295        if rc == 0 {
296            let sn = CStr::from_ptr(serial_num).to_string_lossy().into_owned();
297            free(serial_num as *mut c_void);
298            Ok(sn)
299        } else {
300            Err(lsm_c_error_to_rust(lsm_error))
301        }
302    }
303}
304
305/// Query scsi VPD 0x83 NAA ID.
306pub fn vpd83_get(disk_path: &str) -> Result<String> {
307    let mut vpd83 = std::ptr::null_mut();
308    let mut lsm_error = std::ptr::null_mut();
309
310    unsafe {
311        let disk_path = CString::new(disk_path).expect("CString::new failed");
312        let rc = lsm_local_disk_vpd83_get(disk_path.as_ptr(), &mut vpd83, &mut lsm_error);
313
314        if rc == 0 {
315            let vpd = CStr::from_ptr(vpd83).to_string_lossy().into_owned();
316            free(vpd83 as *mut c_void);
317            Ok(vpd)
318        } else {
319            Err(lsm_c_error_to_rust(lsm_error))
320        }
321    }
322}
323
324/// Possible health status of any disk.
325#[derive(Debug, Copy, Clone, PartialEq, Eq)]
326pub enum HealthStatus {
327    /// Unknown
328    Unknown = -1,
329    /// Device self reports failure
330    Fail = 0,
331    /// Device self reports warning
332    Warn = 1,
333    /// Device self reports good
334    Good = 2,
335}
336
337impl TryFrom<i32> for HealthStatus {
338    type Error = LsmError;
339
340    fn try_from(value: i32) -> Result<Self> {
341        match value {
342            0 => Ok(HealthStatus::Fail),
343            1 => Ok(HealthStatus::Warn),
344            2 => Ok(HealthStatus::Good),
345            _ => Ok(HealthStatus::Unknown),
346        }
347    }
348}
349
350/// Possible values for disk RPM.
351#[repr(i32)]
352#[derive(Debug, Copy, Clone, PartialEq, Eq)]
353pub enum Rpm {
354    /// Unknown
355    Unknown = -1,
356    /// Non-rotational media disk
357    NonRotatingMedium = 0,
358    /// rotational disk with unknown speed
359    UnknownRotationalSpeed = 1,
360    /// Rotating disk with specified speed in RPM
361    Rpm(i32),
362}
363
364impl TryFrom<i32> for Rpm {
365    type Error = LsmError;
366
367    fn try_from(value: i32) -> Result<Self> {
368        let rc = match value {
369            -1 => Rpm::Unknown,
370            0 => Rpm::NonRotatingMedium,
371            1 => Rpm::UnknownRotationalSpeed,
372            _ => Rpm::Rpm(value),
373        };
374        Ok(rc)
375    }
376}
377
378/// Query the health status of the specified disk path.
379pub fn health_get(disk_path: &str) -> Result<HealthStatus> {
380    let mut status: i32 = 0;
381    let mut lsm_error = std::ptr::null_mut();
382
383    unsafe {
384        let disk_path = CString::new(disk_path).expect("CString::new failed");
385        let rc = lsm_local_disk_health_status_get(disk_path.as_ptr(), &mut status, &mut lsm_error);
386
387        if rc == 0 {
388            HealthStatus::try_from(status)
389        } else {
390            Err(lsm_c_error_to_rust(lsm_error))
391        }
392    }
393}
394
395/// Query disk rotation speed.
396pub fn rpm_get(disk_path: &str) -> Result<Rpm> {
397    let mut rpm: i32 = 0;
398    let mut lsm_error = std::ptr::null_mut();
399
400    unsafe {
401        let disk_path = CString::new(disk_path).expect("CString::new failed");
402        let rc = lsm_local_disk_rpm_get(disk_path.as_ptr(), &mut rpm, &mut lsm_error);
403
404        if rc == 0 {
405            Rpm::try_from(rpm)
406        } else {
407            Err(lsm_c_error_to_rust(lsm_error))
408        }
409    }
410}
411
412pub fn list() -> Result<Vec<String>> {
413    // Query local disk paths. Currently, only SCSI, SAS, ATA and NVMe disks will be included.
414    //
415    //! ```rust
416    //! extern crate lsm;
417    //!
418    //! use lsm::local_disk;
419    //!
420    //!fn main() {
421    //!    let disks = local_disk::list().unwrap();
422    //!
423    //!    for d in disks {
424    //!        println!("{}", d);
425    //!    }
426    //!}
427    //! ```
428    let mut disk_paths = std::ptr::null_mut();
429    let mut lsm_error = std::ptr::null_mut();
430    unsafe {
431        let rc = lsm_local_disk_list(&mut disk_paths, &mut lsm_error);
432        if rc == 0 {
433            Ok(c_lsm_string_list_to_vec(disk_paths))
434        } else {
435            Err(lsm_c_error_to_rust(lsm_error))
436        }
437    }
438}
439
440/// Query disk link type.
441pub fn link_type_get(disk_path: &str) -> Result<LinkType> {
442    let mut link_type = LinkType::Unknown;
443    let mut lsm_error = std::ptr::null_mut();
444
445    unsafe {
446        let disk_path = CString::new(disk_path).expect("CString::new failed");
447        let rc = lsm_local_disk_link_type_get(disk_path.as_ptr(), &mut link_type, &mut lsm_error);
448
449        if rc == 0 {
450            Ok(link_type)
451        } else {
452            Err(lsm_c_error_to_rust(lsm_error))
453        }
454    }
455}
456
457fn disk_path_led(disk_path: &str, fault_led: bool, on: bool) -> Result<()> {
458    let mut lsm_error = std::ptr::null_mut();
459    unsafe {
460        let dp = CString::new(disk_path).expect("CString::new failed");
461
462        let rc = match (fault_led, on) {
463            (false, true) => lsm_local_disk_ident_led_on(dp.as_ptr(), &mut lsm_error),
464            (false, false) => lsm_local_disk_ident_led_off(dp.as_ptr(), &mut lsm_error),
465            (true, true) => lsm_local_disk_fault_led_on(dp.as_ptr(), &mut lsm_error),
466            (true, false) => lsm_local_disk_fault_led_off(dp.as_ptr(), &mut lsm_error),
467        };
468
469        if rc == 0 {
470            Ok(())
471        } else {
472            Err(lsm_c_error_to_rust(lsm_error))
473        }
474    }
475}
476
477pub fn ident_led_on(disk_path: &str) -> Result<()> {
478    // Turn the identification LED on for specified disk path.
479    //
480    //! ```rust
481    //! extern crate lsm;
482    //!
483    //! use lsm::local_disk;
484    //!
485    //!fn main() {
486    //!     match local_disk::ident_led_on("/dev/sda") {
487    //!         Err(e) => println!("Unable to turn on identification LED: reason {:?}", e),
488    //!         Ok(_) => println!("Enjoy your blinking LED"),
489    //!     }
490    //!}
491    //! ```
492    disk_path_led(disk_path, false, true)
493}
494
495/// Turn the identification LED off for specified disk path.
496pub fn ident_led_off(disk_path: &str) -> Result<()> {
497    disk_path_led(disk_path, false, false)
498}
499
500/// Turn the fault LED on for specified disk path.
501pub fn fault_led_on(disk_path: &str) -> Result<()> {
502    disk_path_led(disk_path, true, true)
503}
504
505/// Turn the fault LED off for specified disk path.
506pub fn fault_led_off(disk_path: &str) -> Result<()> {
507    disk_path_led(disk_path, true, false)
508}
509
510/// LED has unknown status
511pub const LED_STATUS_UNKNOWN: u32 = 0x0000000000000001;
512/// Identification LED is on
513pub const LED_STATUS_IDENT_ON: u32 = 0x0000000000000002;
514/// Identification LED is off
515pub const LED_STATUS_IDENT_OFF: u32 = 0x0000000000000004;
516/// Identification LED is unknown
517pub const LED_STATUS_IDENT_UNKNOWN: u32 = 0x0000000000000008;
518/// Fault LED is on
519pub const LED_STATUS_FAULT_ON: u32 = 0x0000000000000010;
520/// Fault LED is off
521pub const LED_STATUS_FAULT_OFF: u32 = 0x0000000000000020;
522/// Fault LED is unknown
523pub const LED_STATUS_FAULT_UNKNOWN: u32 = 0x0000000000000040;
524
525/// Retrieve current state of LED for specified disk path.
526///
527/// Note: Not all enclosures support both identification and fault LEDs.
528///
529/// Result is a bit sensitive field, see LED_STATUS_* constants
530pub fn led_status_get(disk_path: &str) -> Result<u32> {
531    let mut lsm_error = std::ptr::null_mut();
532    let mut led_status = 0;
533    let dp = CString::new(disk_path).expect("CString::new failed");
534
535    unsafe {
536        let rc = lsm_local_disk_led_status_get(dp.as_ptr(), &mut led_status, &mut lsm_error);
537        if rc == 0 {
538            Ok(led_status)
539        } else {
540            Err(lsm_c_error_to_rust(lsm_error))
541        }
542    }
543}
544
545/// Query the current negotiated disk link speed in Mbps
546pub fn link_speed_get(disk_path: &str) -> Result<u32> {
547    let mut lsm_error = std::ptr::null_mut();
548    let mut link_speed = 0;
549    let dp = CString::new(disk_path).expect("CString::new failed");
550
551    unsafe {
552        let rc = lsm_local_disk_link_speed_get(dp.as_ptr(), &mut link_speed, &mut lsm_error);
553        if rc == 0 {
554            Ok(link_speed)
555        } else {
556            Err(lsm_c_error_to_rust(lsm_error))
557        }
558    }
559}
560
561fn slot_id_get(slot: *const c_lsm_led_slot) -> String {
562    let id;
563    unsafe {
564        id = lsm_led_slot_id(slot);
565    }
566    assert!(!id.is_null());
567    c_char_ptr_to_string(id)
568}
569
570fn slot_device_get(slot: *const c_lsm_led_slot) -> Option<String> {
571    let device;
572    unsafe {
573        device = lsm_led_slot_device(slot);
574    }
575
576    if device.is_null() {
577        None
578    } else {
579        Some(c_char_ptr_to_string(device))
580    }
581}
582
583/// Opaque type for interacting with LED slots functionality.
584pub struct LedSlots {
585    handle: *mut c_lsm_led_handle,
586    itr: *mut c_lsm_led_slot_itr,
587    curr_slot: *const c_lsm_led_slot,
588    curr_id: String,
589}
590
591impl Drop for LedSlots {
592    fn drop(&mut self) {
593        unsafe {
594            lsm_led_slot_iterator_free(self.handle, self.itr);
595            self.itr = std::ptr::null_mut();
596            lsm_led_handle_free(self.handle);
597            self.handle = std::ptr::null_mut();
598            self.curr_slot = std::ptr::null_mut();
599        }
600    }
601}
602
603impl LedSlots {
604    fn find_slot(&mut self, id: &str) -> bool {
605        let mut rc = false;
606        unsafe {
607            // When processing LEDs sequentially, this will help
608            if !self.itr.is_null() {
609                let slot = lsm_led_slot_next(self.handle, self.itr);
610                if !slot.is_null() {
611                    let slot_id = slot_id_get(slot);
612                    if id == slot_id {
613                        self.curr_slot = slot;
614                        self.curr_id = slot_id;
615                        return true;
616                    }
617                }
618            }
619
620            lsm_led_slot_iterator_reset(self.handle, self.itr);
621            loop {
622                let slot = lsm_led_slot_next(self.handle, self.itr);
623                if slot.is_null() {
624                    break;
625                } else {
626                    let slot_id = slot_id_get(slot);
627                    if id == slot_id {
628                        rc = true;
629                        self.curr_slot = slot;
630                        self.curr_id = slot_id;
631                        break;
632                    }
633                }
634            }
635        }
636        rc
637    }
638
639    fn slot_set(&mut self, id: &str) {
640        if self.curr_id != id {
641            assert!(self.find_slot(id));
642        }
643    }
644
645    /// Create a new instance of LED slots
646    pub fn new() -> Result<Self> {
647        unsafe {
648            let mut handle = std::ptr::null_mut();
649            let mut itr = std::ptr::null_mut();
650            let mut lsm_error = std::ptr::null_mut();
651
652            let handle_rc = lsm_led_handle_get(&mut handle, 0);
653
654            if handle_rc == 0 {
655                let itr_rc = lsm_led_slot_iterator_get(handle, &mut itr, &mut lsm_error, 0);
656
657                if itr_rc == 0 {
658                    Ok(Self {
659                        handle,
660                        itr,
661                        curr_slot: std::ptr::null_mut(),
662                        curr_id: String::new(),
663                    })
664                } else {
665                    // We failed to get the iterator, but we did get the handle, so free it!
666                    lsm_led_handle_free(handle);
667                    Err(lsm_c_error_to_rust(lsm_error))
668                }
669            } else {
670                Err(lsm_error_code_to_lsm_error(
671                    handle_rc,
672                    String::from("LedSlot::new() failed, no additional information!"),
673                ))
674            }
675        }
676    }
677
678    /// Retrieve the slots
679    pub fn slots_get(&mut self) -> Vec<LedSlot> {
680        let mut rc = Vec::new();
681
682        unsafe {
683            lsm_led_slot_iterator_reset(self.handle, self.itr);
684
685            loop {
686                let slot = lsm_led_slot_next(self.handle, self.itr);
687                if !slot.is_null() {
688                    let entry = LedSlot::new(slot);
689                    rc.push(entry);
690                } else {
691                    break;
692                }
693            }
694        }
695
696        rc
697    }
698
699    /// Retrieve the LED status for the specified slot.
700    pub fn slot_status_get(&mut self, slot: &LedSlot) -> u32 {
701        self.slot_set(&slot.id());
702        unsafe { lsm_led_slot_status_get(self.curr_slot) }
703    }
704
705    /// Set the status of the specified slot, bit sensitive input state.
706    ///
707    /// Please note that not all LED hardware supports both
708    /// identification and fault LEDs.
709    ///
710    /// Using this API, please specify what you
711    /// would like regardless of support and the hardware will adhere to your request as best it can.
712    /// * LED_STATUS_IDENT_ON => Implies fault off
713    /// * LED_STATUS_FAULT_ON => Implies ident and fault on
714    /// * LED_STATUS_IDENT_OFF => Implies both ident and fault are off
715    /// * LED_STATUS_FAULT_OFF => Implies both ident and fault are off
716    /// * LSM_DISK_LED_STATUS_IDENT_OFF|LSM_DISK_LED_STATUS_FAULT_OFF
717    /// * LED_STATUS_IDENT_ON|LSM_DISK_LED_STATUS_FAULT_OFF
718    /// * LED_STATUS_FAULT_ON|LSM_DISK_LED_STATUS_IDENT_OFF
719    /// * LED_STATUS_IDENT_ON|LSM_DISK_LED_STATUS_FAULT_ON
720    ///
721    pub fn slot_status_set(&mut self, slot: &LedSlot, state: u32) -> Result<()> {
722        self.slot_set(&slot.id());
723
724        let mut lsm_error = std::ptr::null_mut();
725
726        unsafe {
727            let rc = lsm_led_slot_status_set(self.handle, self.curr_slot, state, &mut lsm_error, 0);
728            if rc == 0 {
729                Ok(())
730            } else {
731                Err(lsm_c_error_to_rust(lsm_error))
732            }
733        }
734    }
735}
736
737/// Information about specific slot
738///
739/// Note: you can retrieve/set set of LED with out a block device present
740pub struct LedSlot {
741    /// Slot ID
742    id: String,
743    /// Device node for slot which may not be present (used for correlation)
744    dev: Option<String>,
745}
746impl LedSlot {
747    fn new(slot: *const c_lsm_led_slot) -> Self {
748        Self {
749            id: slot_id_get(slot),
750            dev: slot_device_get(slot),
751        }
752    }
753
754    /// Slot ID of slot (this is not persistent across boots or dynamic reconfiguration)
755    pub fn id(&self) -> String {
756        self.id.clone()
757    }
758
759    /// Device node for block device which may not be present (you don't need a functional disk to change LED state)
760    pub fn device(&self) -> Option<String> {
761        self.dev.clone()
762    }
763}