Skip to main content

pmbus_adapter/
lib.rs

1#![no_std]
2
3pub mod commands;
4pub mod error;
5pub mod formats;
6pub mod status;
7pub mod vout_mode;
8
9use embedded_hal_async::i2c::I2c;
10use heapless::Vec;
11use smbus_adapter::SmbusAdaptor;
12
13pub use commands::CommandCode;
14pub use error::PmbusError;
15pub use formats::{DirectCoefficients, Linear11, ULinear16};
16pub use status::*;
17pub use vout_mode::{VoutMode, VoutModeType};
18
19// ---------------------------------------------------------------------------
20// Macros to generate repetitive PMBus command methods
21// ---------------------------------------------------------------------------
22
23/// Generate a send-byte command (no data payload).
24macro_rules! pmbus_send_byte {
25    ($name:ident, $cmd:ident) => {
26        pub async fn $name(&mut self, addr: u8) -> Result<(), BUS::Error> {
27            self.send_cmd(addr, CommandCode::$cmd).await
28        }
29    };
30}
31
32/// Generate read-byte and write-byte pair.
33macro_rules! pmbus_byte_rw {
34    ($set:ident, $get:ident, $cmd:ident) => {
35        pub async fn $set(&mut self, addr: u8, data: u8) -> Result<(), BUS::Error> {
36            self.write_cmd_byte(addr, CommandCode::$cmd, data).await
37        }
38        pub async fn $get(&mut self, addr: u8) -> Result<u8, BUS::Error> {
39            self.read_cmd_byte(addr, CommandCode::$cmd).await
40        }
41    };
42}
43
44/// Generate write-byte only.
45macro_rules! pmbus_write_byte_only {
46    ($name:ident, $cmd:ident) => {
47        pub async fn $name(&mut self, addr: u8, data: u8) -> Result<(), BUS::Error> {
48            self.write_cmd_byte(addr, CommandCode::$cmd, data).await
49        }
50    };
51}
52
53/// Generate read-byte only.
54macro_rules! pmbus_read_byte_only {
55    ($name:ident, $cmd:ident) => {
56        pub async fn $name(&mut self, addr: u8) -> Result<u8, BUS::Error> {
57            self.read_cmd_byte(addr, CommandCode::$cmd).await
58        }
59    };
60}
61
62/// Generate read-word and write-word pair.
63macro_rules! pmbus_word_rw {
64    ($set:ident, $get:ident, $cmd:ident) => {
65        pub async fn $set(&mut self, addr: u8, data: u16) -> Result<(), BUS::Error> {
66            self.write_cmd_word(addr, CommandCode::$cmd, data).await
67        }
68        pub async fn $get(&mut self, addr: u8) -> Result<u16, BUS::Error> {
69            self.read_cmd_word(addr, CommandCode::$cmd).await
70        }
71    };
72}
73
74/// Generate read-word only.
75macro_rules! pmbus_read_word_only {
76    ($name:ident, $cmd:ident) => {
77        pub async fn $name(&mut self, addr: u8) -> Result<u16, BUS::Error> {
78            self.read_cmd_word(addr, CommandCode::$cmd).await
79        }
80    };
81}
82
83/// Generate block read and block write pair.
84macro_rules! pmbus_block_rw {
85    ($set:ident, $get:ident, $cmd:ident) => {
86        pub async fn $set(&mut self, addr: u8, data: &[u8]) -> Result<(), BUS::Error> {
87            self.block_write_cmd(addr, CommandCode::$cmd, data).await
88        }
89        pub async fn $get(&mut self, addr: u8) -> Result<Vec<u8, 32>, BUS::Error> {
90            self.block_read_cmd(addr, CommandCode::$cmd).await
91        }
92    };
93}
94
95/// Generate block read only.
96macro_rules! pmbus_block_read_only {
97    ($name:ident, $cmd:ident) => {
98        pub async fn $name(&mut self, addr: u8) -> Result<Vec<u8, 32>, BUS::Error> {
99            self.block_read_cmd(addr, CommandCode::$cmd).await
100        }
101    };
102}
103
104// ---------------------------------------------------------------------------
105// PmbusAdaptor
106// ---------------------------------------------------------------------------
107
108/// A PMBus protocol adapter that wraps an `SmbusAdaptor`.
109///
110/// Provides typed methods for every standard PMBus 1.4 command. The device
111/// address is passed per-call (not stored), matching the smbus-adapter pattern.
112pub struct PmbusAdaptor<BUS: I2c> {
113    smbus: SmbusAdaptor<BUS>,
114}
115
116impl<BUS: I2c + 'static> PmbusAdaptor<BUS> {
117    /// Create a new PMBus adapter wrapping the given SMBus adapter.
118    pub fn new(smbus: SmbusAdaptor<BUS>) -> Self {
119        Self { smbus }
120    }
121
122    /// Consume self and return the inner `SmbusAdaptor`.
123    pub fn release(self) -> SmbusAdaptor<BUS> {
124        self.smbus
125    }
126
127    /// Borrow the inner `SmbusAdaptor` mutably.
128    pub fn inner(&mut self) -> &mut SmbusAdaptor<BUS> {
129        &mut self.smbus
130    }
131
132    // -----------------------------------------------------------------------
133    // Private helpers
134    // -----------------------------------------------------------------------
135
136    async fn send_cmd(&mut self, addr: u8, cmd: CommandCode) -> Result<(), BUS::Error> {
137        self.smbus.send_byte(addr, cmd.code()).await
138    }
139
140    async fn write_cmd_byte(
141        &mut self,
142        addr: u8,
143        cmd: CommandCode,
144        data: u8,
145    ) -> Result<(), BUS::Error> {
146        self.smbus.write_byte(addr, cmd.code(), data).await
147    }
148
149    async fn read_cmd_byte(&mut self, addr: u8, cmd: CommandCode) -> Result<u8, BUS::Error> {
150        self.smbus.read_byte(addr, cmd.code()).await
151    }
152
153    async fn write_cmd_word(
154        &mut self,
155        addr: u8,
156        cmd: CommandCode,
157        data: u16,
158    ) -> Result<(), BUS::Error> {
159        self.smbus.write_word(addr, cmd.code(), data).await
160    }
161
162    async fn read_cmd_word(&mut self, addr: u8, cmd: CommandCode) -> Result<u16, BUS::Error> {
163        self.smbus.read_word(addr, cmd.code()).await
164    }
165
166    async fn block_write_cmd(
167        &mut self,
168        addr: u8,
169        cmd: CommandCode,
170        data: &[u8],
171    ) -> Result<(), BUS::Error> {
172        self.smbus.block_write(addr, cmd.code(), data).await
173    }
174
175    async fn block_read_cmd(
176        &mut self,
177        addr: u8,
178        cmd: CommandCode,
179    ) -> Result<Vec<u8, 32>, BUS::Error> {
180        self.smbus.block_read(addr, cmd.code()).await
181    }
182
183    async fn block_process_call_cmd(
184        &mut self,
185        addr: u8,
186        cmd: CommandCode,
187        data: &[u8],
188    ) -> Result<Vec<u8, 32>, BUS::Error> {
189        self.smbus
190            .block_read_process_call(addr, cmd.code(), data)
191            .await
192    }
193
194    // =======================================================================
195    // Send-byte commands (no data)
196    // =======================================================================
197
198    pmbus_send_byte!(clear_faults, ClearFaults);
199    pmbus_send_byte!(store_default_all, StoreDefaultAll);
200    pmbus_send_byte!(restore_default_all, RestoreDefaultAll);
201    pmbus_send_byte!(store_user_all, StoreUserAll);
202    pmbus_send_byte!(restore_user_all, RestoreUserAll);
203
204    // =======================================================================
205    // Byte read/write commands
206    // =======================================================================
207
208    pmbus_byte_rw!(set_page, get_page, Page);
209    pmbus_byte_rw!(set_operation, get_operation, Operation);
210    pmbus_byte_rw!(set_on_off_config, get_on_off_config, OnOffConfig);
211    pmbus_byte_rw!(set_phase, get_phase, Phase);
212    pmbus_byte_rw!(set_write_protect, get_write_protect, WriteProtect);
213    pmbus_byte_rw!(set_power_mode, get_power_mode, PowerMode);
214    pmbus_byte_rw!(set_fan_config_12, get_fan_config_12, FanConfig12);
215    pmbus_byte_rw!(set_fan_config_34, get_fan_config_34, FanConfig34);
216
217    // Fault responses (byte r/w)
218    pmbus_byte_rw!(
219        set_vout_ov_fault_response,
220        get_vout_ov_fault_response,
221        VoutOvFaultResponse
222    );
223    pmbus_byte_rw!(
224        set_vout_uv_fault_response,
225        get_vout_uv_fault_response,
226        VoutUvFaultResponse
227    );
228    pmbus_byte_rw!(
229        set_iout_oc_fault_response,
230        get_iout_oc_fault_response,
231        IoutOcFaultResponse
232    );
233    pmbus_byte_rw!(
234        set_iout_oc_lv_fault_response,
235        get_iout_oc_lv_fault_response,
236        IoutOcLvFaultResponse
237    );
238    pmbus_byte_rw!(
239        set_iout_uc_fault_response,
240        get_iout_uc_fault_response,
241        IoutUcFaultResponse
242    );
243    pmbus_byte_rw!(
244        set_ot_fault_response,
245        get_ot_fault_response,
246        OtFaultResponse
247    );
248    pmbus_byte_rw!(
249        set_ut_fault_response,
250        get_ut_fault_response,
251        UtFaultResponse
252    );
253    pmbus_byte_rw!(
254        set_vin_ov_fault_response,
255        get_vin_ov_fault_response,
256        VinOvFaultResponse
257    );
258    pmbus_byte_rw!(
259        set_vin_uv_fault_response,
260        get_vin_uv_fault_response,
261        VinUvFaultResponse
262    );
263    pmbus_byte_rw!(
264        set_iin_oc_fault_response,
265        get_iin_oc_fault_response,
266        IinOcFaultResponse
267    );
268    pmbus_byte_rw!(
269        set_ton_max_fault_response,
270        get_ton_max_fault_response,
271        TonMaxFaultResponse
272    );
273    pmbus_byte_rw!(
274        set_pout_op_fault_response,
275        get_pout_op_fault_response,
276        PoutOpFaultResponse
277    );
278
279    // Write-byte only
280    pmbus_write_byte_only!(store_default_code, StoreDefaultCode);
281    pmbus_write_byte_only!(restore_default_code, RestoreDefaultCode);
282    pmbus_write_byte_only!(store_user_code, StoreUserCode);
283    pmbus_write_byte_only!(restore_user_code, RestoreUserCode);
284
285    // Read-byte only
286    pmbus_read_byte_only!(get_capability, Capability);
287    pmbus_read_byte_only!(get_pmbus_revision, PmbusRevision);
288    pmbus_read_byte_only!(get_mfr_pin_accuracy, MfrPinAccuracy);
289
290    // =======================================================================
291    // Word read/write commands
292    // =======================================================================
293
294    // Output voltage
295    pmbus_word_rw!(set_vout_command, get_vout_command, VoutCommand);
296    pmbus_word_rw!(set_vout_trim, get_vout_trim, VoutTrim);
297    pmbus_word_rw!(set_vout_cal_offset, get_vout_cal_offset, VoutCalOffset);
298    pmbus_word_rw!(set_vout_max, get_vout_max, VoutMax);
299    pmbus_word_rw!(set_vout_margin_high, get_vout_margin_high, VoutMarginHigh);
300    pmbus_word_rw!(set_vout_margin_low, get_vout_margin_low, VoutMarginLow);
301    pmbus_word_rw!(
302        set_vout_transition_rate,
303        get_vout_transition_rate,
304        VoutTransitionRate
305    );
306    pmbus_word_rw!(set_vout_droop, get_vout_droop, VoutDroop);
307    pmbus_word_rw!(set_vout_scale_loop, get_vout_scale_loop, VoutScaleLoop);
308    pmbus_word_rw!(
309        set_vout_scale_monitor,
310        get_vout_scale_monitor,
311        VoutScaleMonitor
312    );
313    pmbus_word_rw!(set_vout_min, get_vout_min, VoutMin);
314
315    // Power / switching
316    pmbus_word_rw!(set_pout_max, get_pout_max, PoutMax);
317    pmbus_word_rw!(set_max_duty, get_max_duty, MaxDuty);
318    pmbus_word_rw!(set_frequency_switch, get_frequency_switch, FrequencySwitch);
319    pmbus_word_rw!(set_vin_on, get_vin_on, VinOn);
320    pmbus_word_rw!(set_vin_off, get_vin_off, VinOff);
321    pmbus_word_rw!(set_interleave, get_interleave, Interleave);
322    pmbus_word_rw!(set_iout_cal_gain, get_iout_cal_gain, IoutCalGain);
323    pmbus_word_rw!(set_iout_cal_offset, get_iout_cal_offset, IoutCalOffset);
324
325    // Fan commands
326    pmbus_word_rw!(set_fan_command_1, get_fan_command_1, FanCommand1);
327    pmbus_word_rw!(set_fan_command_2, get_fan_command_2, FanCommand2);
328    pmbus_word_rw!(set_fan_command_3, get_fan_command_3, FanCommand3);
329    pmbus_word_rw!(set_fan_command_4, get_fan_command_4, FanCommand4);
330
331    // Fault/warn limits (word r/w)
332    pmbus_word_rw!(
333        set_vout_ov_fault_limit,
334        get_vout_ov_fault_limit,
335        VoutOvFaultLimit
336    );
337    pmbus_word_rw!(
338        set_vout_ov_warn_limit,
339        get_vout_ov_warn_limit,
340        VoutOvWarnLimit
341    );
342    pmbus_word_rw!(
343        set_vout_uv_warn_limit,
344        get_vout_uv_warn_limit,
345        VoutUvWarnLimit
346    );
347    pmbus_word_rw!(
348        set_vout_uv_fault_limit,
349        get_vout_uv_fault_limit,
350        VoutUvFaultLimit
351    );
352    pmbus_word_rw!(
353        set_iout_oc_fault_limit,
354        get_iout_oc_fault_limit,
355        IoutOcFaultLimit
356    );
357    pmbus_word_rw!(
358        set_iout_oc_lv_fault_limit,
359        get_iout_oc_lv_fault_limit,
360        IoutOcLvFaultLimit
361    );
362    pmbus_word_rw!(
363        set_iout_oc_warn_limit,
364        get_iout_oc_warn_limit,
365        IoutOcWarnLimit
366    );
367    pmbus_word_rw!(
368        set_iout_uc_fault_limit,
369        get_iout_uc_fault_limit,
370        IoutUcFaultLimit
371    );
372    pmbus_word_rw!(set_ot_fault_limit, get_ot_fault_limit, OtFaultLimit);
373    pmbus_word_rw!(set_ot_warn_limit, get_ot_warn_limit, OtWarnLimit);
374    pmbus_word_rw!(set_ut_warn_limit, get_ut_warn_limit, UtWarnLimit);
375    pmbus_word_rw!(set_ut_fault_limit, get_ut_fault_limit, UtFaultLimit);
376    pmbus_word_rw!(
377        set_vin_ov_fault_limit,
378        get_vin_ov_fault_limit,
379        VinOvFaultLimit
380    );
381    pmbus_word_rw!(set_vin_ov_warn_limit, get_vin_ov_warn_limit, VinOvWarnLimit);
382    pmbus_word_rw!(set_vin_uv_warn_limit, get_vin_uv_warn_limit, VinUvWarnLimit);
383    pmbus_word_rw!(
384        set_vin_uv_fault_limit,
385        get_vin_uv_fault_limit,
386        VinUvFaultLimit
387    );
388    pmbus_word_rw!(
389        set_iin_oc_fault_limit,
390        get_iin_oc_fault_limit,
391        IinOcFaultLimit
392    );
393    pmbus_word_rw!(set_iin_oc_warn_limit, get_iin_oc_warn_limit, IinOcWarnLimit);
394    pmbus_word_rw!(set_power_good_on, get_power_good_on, PowerGoodOn);
395    pmbus_word_rw!(set_power_good_off, get_power_good_off, PowerGoodOff);
396    pmbus_word_rw!(set_ton_delay, get_ton_delay, TonDelay);
397    pmbus_word_rw!(set_ton_rise, get_ton_rise, TonRise);
398    pmbus_word_rw!(
399        set_ton_max_fault_limit,
400        get_ton_max_fault_limit,
401        TonMaxFaultLimit
402    );
403    pmbus_word_rw!(set_toff_delay, get_toff_delay, ToffDelay);
404    pmbus_word_rw!(set_toff_fall, get_toff_fall, ToffFall);
405    pmbus_word_rw!(
406        set_toff_max_warn_limit,
407        get_toff_max_warn_limit,
408        ToffMaxWarnLimit
409    );
410    pmbus_word_rw!(
411        set_pout_op_fault_limit,
412        get_pout_op_fault_limit,
413        PoutOpFaultLimit
414    );
415    pmbus_word_rw!(
416        set_pout_op_warn_limit,
417        get_pout_op_warn_limit,
418        PoutOpWarnLimit
419    );
420    pmbus_word_rw!(set_pin_op_warn_limit, get_pin_op_warn_limit, PinOpWarnLimit);
421
422    // Zone / KWH config
423    pmbus_word_rw!(set_zone_config, get_zone_config, ZoneConfig);
424    pmbus_word_rw!(set_zone_active, get_zone_active, ZoneActive);
425    pmbus_word_rw!(set_read_kwh_config, get_read_kwh_config, ReadKwhConfig);
426
427    // MFR telemetry limits (word r/w)
428    pmbus_word_rw!(set_mfr_vin_min, get_mfr_vin_min, MfrVinMin);
429    pmbus_word_rw!(set_mfr_vin_max, get_mfr_vin_max, MfrVinMax);
430    pmbus_word_rw!(set_mfr_iin_max, get_mfr_iin_max, MfrIinMax);
431    pmbus_word_rw!(set_mfr_pin_max, get_mfr_pin_max, MfrPinMax);
432    pmbus_word_rw!(set_mfr_vout_min, get_mfr_vout_min, MfrVoutMin);
433    pmbus_word_rw!(set_mfr_vout_max, get_mfr_vout_max, MfrVoutMax);
434    pmbus_word_rw!(set_mfr_iout_max, get_mfr_iout_max, MfrIoutMax);
435    pmbus_word_rw!(set_mfr_pout_max, get_mfr_pout_max, MfrPoutMax);
436    pmbus_word_rw!(set_mfr_tambient_max, get_mfr_tambient_max, MfrTambientMax);
437    pmbus_word_rw!(set_mfr_tambient_min, get_mfr_tambient_min, MfrTambientMin);
438    pmbus_word_rw!(set_mfr_max_temp_1, get_mfr_max_temp_1, MfrMaxTemp1);
439    pmbus_word_rw!(set_mfr_max_temp_2, get_mfr_max_temp_2, MfrMaxTemp2);
440    pmbus_word_rw!(set_mfr_max_temp_3, get_mfr_max_temp_3, MfrMaxTemp3);
441
442    // =======================================================================
443    // Read-word only (sensor telemetry)
444    // =======================================================================
445
446    pmbus_read_word_only!(read_vin, ReadVin);
447    pmbus_read_word_only!(read_iin, ReadIin);
448    pmbus_read_word_only!(read_vcap, ReadVcap);
449    pmbus_read_word_only!(read_vout, ReadVout);
450    pmbus_read_word_only!(read_iout, ReadIout);
451    pmbus_read_word_only!(read_temperature_1, ReadTemperature1);
452    pmbus_read_word_only!(read_temperature_2, ReadTemperature2);
453    pmbus_read_word_only!(read_temperature_3, ReadTemperature3);
454    pmbus_read_word_only!(read_fan_speed_1, ReadFanSpeed1);
455    pmbus_read_word_only!(read_fan_speed_2, ReadFanSpeed2);
456    pmbus_read_word_only!(read_fan_speed_3, ReadFanSpeed3);
457    pmbus_read_word_only!(read_fan_speed_4, ReadFanSpeed4);
458    pmbus_read_word_only!(read_duty_cycle, ReadDutyCycle);
459    pmbus_read_word_only!(read_frequency, ReadFrequency);
460    pmbus_read_word_only!(read_pout, ReadPout);
461    pmbus_read_word_only!(read_pin, ReadPin);
462
463    // =======================================================================
464    // Block read/write commands
465    // =======================================================================
466
467    pmbus_block_rw!(set_mfr_id, get_mfr_id, MfrId);
468    pmbus_block_rw!(set_mfr_model, get_mfr_model, MfrModel);
469    pmbus_block_rw!(set_mfr_revision, get_mfr_revision, MfrRevision);
470    pmbus_block_rw!(set_mfr_location, get_mfr_location, MfrLocation);
471    pmbus_block_rw!(set_mfr_date, get_mfr_date, MfrDate);
472    pmbus_block_rw!(set_mfr_serial, get_mfr_serial, MfrSerial);
473    pmbus_block_read_only!(get_app_profile_support, AppProfileSupport);
474    pmbus_block_read_only!(get_ic_device_id, IcDeviceId);
475    pmbus_block_read_only!(get_ic_device_rev, IcDeviceRev);
476    pmbus_block_read_only!(get_mfr_efficiency_ll, MfrEfficiencyLl);
477    pmbus_block_read_only!(get_mfr_efficiency_hl, MfrEfficiencyHl);
478    pmbus_block_read_only!(read_ein, ReadEin);
479    pmbus_block_read_only!(read_eout, ReadEout);
480
481    // =======================================================================
482    // User data — indexed block read/write
483    // =======================================================================
484
485    /// Write user data block at the given index (0-15).
486    pub async fn set_user_data(
487        &mut self,
488        addr: u8,
489        index: u8,
490        data: &[u8],
491    ) -> Result<(), BUS::Error> {
492        let code = CommandCode::UserData00.code() + (index & 0x0F);
493        self.smbus.block_write(addr, code, data).await
494    }
495
496    /// Read user data block at the given index (0-15).
497    pub async fn get_user_data(&mut self, addr: u8, index: u8) -> Result<Vec<u8, 32>, BUS::Error> {
498        let code = CommandCode::UserData00.code() + (index & 0x0F);
499        self.smbus.block_read(addr, code).await
500    }
501
502    // =======================================================================
503    // Status registers — typed accessors
504    // =======================================================================
505
506    /// Read STATUS_BYTE (0x78).
507    pub async fn get_status_byte(&mut self, addr: u8) -> Result<StatusByte, BUS::Error> {
508        let raw = self.read_cmd_byte(addr, CommandCode::StatusByte).await?;
509        Ok(StatusByte::from_raw(raw))
510    }
511
512    /// Write STATUS_BYTE to clear bits (0x78).
513    pub async fn set_status_byte(
514        &mut self,
515        addr: u8,
516        status: StatusByte,
517    ) -> Result<(), BUS::Error> {
518        self.write_cmd_byte(addr, CommandCode::StatusByte, status.bits())
519            .await
520    }
521
522    /// Read STATUS_WORD (0x79).
523    pub async fn get_status_word(&mut self, addr: u8) -> Result<StatusWord, BUS::Error> {
524        let raw = self.read_cmd_word(addr, CommandCode::StatusWord).await?;
525        Ok(StatusWord::from_raw(raw))
526    }
527
528    /// Write STATUS_WORD to clear bits (0x79).
529    pub async fn set_status_word(
530        &mut self,
531        addr: u8,
532        status: StatusWord,
533    ) -> Result<(), BUS::Error> {
534        self.write_cmd_word(addr, CommandCode::StatusWord, status.bits())
535            .await
536    }
537
538    /// Read STATUS_VOUT (0x7A).
539    pub async fn get_status_vout(&mut self, addr: u8) -> Result<StatusVout, BUS::Error> {
540        let raw = self.read_cmd_byte(addr, CommandCode::StatusVout).await?;
541        Ok(StatusVout::from_raw(raw))
542    }
543
544    /// Write STATUS_VOUT to clear bits (0x7A).
545    pub async fn set_status_vout(
546        &mut self,
547        addr: u8,
548        status: StatusVout,
549    ) -> Result<(), BUS::Error> {
550        self.write_cmd_byte(addr, CommandCode::StatusVout, status.bits())
551            .await
552    }
553
554    /// Read STATUS_IOUT (0x7B).
555    pub async fn get_status_iout(&mut self, addr: u8) -> Result<StatusIout, BUS::Error> {
556        let raw = self.read_cmd_byte(addr, CommandCode::StatusIout).await?;
557        Ok(StatusIout::from_raw(raw))
558    }
559
560    /// Write STATUS_IOUT to clear bits (0x7B).
561    pub async fn set_status_iout(
562        &mut self,
563        addr: u8,
564        status: StatusIout,
565    ) -> Result<(), BUS::Error> {
566        self.write_cmd_byte(addr, CommandCode::StatusIout, status.bits())
567            .await
568    }
569
570    /// Read STATUS_INPUT (0x7C).
571    pub async fn get_status_input(&mut self, addr: u8) -> Result<StatusInput, BUS::Error> {
572        let raw = self.read_cmd_byte(addr, CommandCode::StatusInput).await?;
573        Ok(StatusInput::from_raw(raw))
574    }
575
576    /// Write STATUS_INPUT to clear bits (0x7C).
577    pub async fn set_status_input(
578        &mut self,
579        addr: u8,
580        status: StatusInput,
581    ) -> Result<(), BUS::Error> {
582        self.write_cmd_byte(addr, CommandCode::StatusInput, status.bits())
583            .await
584    }
585
586    /// Read STATUS_TEMPERATURE (0x7D).
587    pub async fn get_status_temperature(
588        &mut self,
589        addr: u8,
590    ) -> Result<StatusTemperature, BUS::Error> {
591        let raw = self
592            .read_cmd_byte(addr, CommandCode::StatusTemperature)
593            .await?;
594        Ok(StatusTemperature::from_raw(raw))
595    }
596
597    /// Write STATUS_TEMPERATURE to clear bits (0x7D).
598    pub async fn set_status_temperature(
599        &mut self,
600        addr: u8,
601        status: StatusTemperature,
602    ) -> Result<(), BUS::Error> {
603        self.write_cmd_byte(addr, CommandCode::StatusTemperature, status.bits())
604            .await
605    }
606
607    /// Read STATUS_CML (0x7E).
608    pub async fn get_status_cml(&mut self, addr: u8) -> Result<StatusCml, BUS::Error> {
609        let raw = self.read_cmd_byte(addr, CommandCode::StatusCml).await?;
610        Ok(StatusCml::from_raw(raw))
611    }
612
613    /// Write STATUS_CML to clear bits (0x7E).
614    pub async fn set_status_cml(&mut self, addr: u8, status: StatusCml) -> Result<(), BUS::Error> {
615        self.write_cmd_byte(addr, CommandCode::StatusCml, status.bits())
616            .await
617    }
618
619    /// Read STATUS_OTHER (0x7F).
620    pub async fn get_status_other(&mut self, addr: u8) -> Result<StatusOther, BUS::Error> {
621        let raw = self.read_cmd_byte(addr, CommandCode::StatusOther).await?;
622        Ok(StatusOther::from_raw(raw))
623    }
624
625    /// Write STATUS_OTHER to clear bits (0x7F).
626    pub async fn set_status_other(
627        &mut self,
628        addr: u8,
629        status: StatusOther,
630    ) -> Result<(), BUS::Error> {
631        self.write_cmd_byte(addr, CommandCode::StatusOther, status.bits())
632            .await
633    }
634
635    /// Read STATUS_MFR_SPECIFIC (0x80).
636    pub async fn get_status_mfr_specific(&mut self, addr: u8) -> Result<u8, BUS::Error> {
637        self.read_cmd_byte(addr, CommandCode::StatusMfrSpecific)
638            .await
639    }
640
641    /// Write STATUS_MFR_SPECIFIC to clear bits (0x80).
642    pub async fn set_status_mfr_specific(&mut self, addr: u8, data: u8) -> Result<(), BUS::Error> {
643        self.write_cmd_byte(addr, CommandCode::StatusMfrSpecific, data)
644            .await
645    }
646
647    /// Read STATUS_FANS_1_2 (0x81).
648    pub async fn get_status_fans_12(&mut self, addr: u8) -> Result<StatusFans12, BUS::Error> {
649        let raw = self.read_cmd_byte(addr, CommandCode::StatusFans12).await?;
650        Ok(StatusFans12::from_raw(raw))
651    }
652
653    /// Write STATUS_FANS_1_2 to clear bits (0x81).
654    pub async fn set_status_fans_12(
655        &mut self,
656        addr: u8,
657        status: StatusFans12,
658    ) -> Result<(), BUS::Error> {
659        self.write_cmd_byte(addr, CommandCode::StatusFans12, status.bits())
660            .await
661    }
662
663    /// Read STATUS_FANS_3_4 (0x82).
664    pub async fn get_status_fans_34(&mut self, addr: u8) -> Result<StatusFans34, BUS::Error> {
665        let raw = self.read_cmd_byte(addr, CommandCode::StatusFans34).await?;
666        Ok(StatusFans34::from_raw(raw))
667    }
668
669    /// Write STATUS_FANS_3_4 to clear bits (0x82).
670    pub async fn set_status_fans_34(
671        &mut self,
672        addr: u8,
673        status: StatusFans34,
674    ) -> Result<(), BUS::Error> {
675        self.write_cmd_byte(addr, CommandCode::StatusFans34, status.bits())
676            .await
677    }
678
679    // =======================================================================
680    // Special commands — manual implementations
681    // =======================================================================
682
683    /// Read VOUT_MODE (0x20) and parse into `VoutMode`.
684    pub async fn get_vout_mode(&mut self, addr: u8) -> Result<VoutMode, BUS::Error> {
685        let raw = self.read_cmd_byte(addr, CommandCode::VoutMode).await?;
686        Ok(VoutMode::from_raw(raw))
687    }
688
689    /// Write VOUT_MODE (0x20) from a `VoutMode` value.
690    pub async fn set_vout_mode(&mut self, addr: u8, mode: VoutMode) -> Result<(), BUS::Error> {
691        self.write_cmd_byte(addr, CommandCode::VoutMode, mode.to_raw())
692            .await
693    }
694
695    /// Read COEFFICIENTS (0x30) using block read/write process call.
696    ///
697    /// `query` is the 1-byte code identifying which coefficient set to read.
698    pub async fn get_coefficients(
699        &mut self,
700        addr: u8,
701        query: u8,
702    ) -> Result<DirectCoefficients, PmbusError<BUS::Error>> {
703        let resp = self
704            .block_process_call_cmd(addr, CommandCode::Coefficients, &[query])
705            .await?;
706        // Response: [byte_count, m_low, m_high, b_low, b_high, r]
707        if resp.len() < 6 {
708            return Err(PmbusError::InvalidResponseLength);
709        }
710        DirectCoefficients::from_coefficients_response(&resp[1..6])
711            .ok_or(PmbusError::InvalidResponseLength)
712    }
713
714    /// Execute QUERY command (0x1A) — asks the device about a command's support.
715    pub async fn query(&mut self, addr: u8, command: u8) -> Result<u8, BUS::Error> {
716        self.smbus
717            .process_call(addr, CommandCode::Query.code(), command as u16)
718            .await
719            .map(|w| w as u8)
720    }
721
722    /// Read SMBALERT_MASK (0x1B) using process call.
723    pub async fn get_smbalert_mask(
724        &mut self,
725        addr: u8,
726        status_register: u8,
727    ) -> Result<u8, BUS::Error> {
728        self.smbus
729            .process_call(
730                addr,
731                CommandCode::SmbalertMask.code(),
732                status_register as u16,
733            )
734            .await
735            .map(|w| w as u8)
736    }
737
738    /// Write SMBALERT_MASK (0x1B).
739    pub async fn set_smbalert_mask(&mut self, addr: u8, data: u16) -> Result<(), BUS::Error> {
740        self.write_cmd_word(addr, CommandCode::SmbalertMask, data)
741            .await
742    }
743
744    /// Read PAGE_PLUS_READ (0x06) — reads a byte from a specific page in one transaction.
745    pub async fn page_plus_read(
746        &mut self,
747        addr: u8,
748        page: u8,
749        command: u8,
750    ) -> Result<Vec<u8, 32>, BUS::Error> {
751        self.block_process_call_cmd(addr, CommandCode::PagePlusRead, &[page, command])
752            .await
753    }
754
755    /// Write PAGE_PLUS_WRITE (0x05) — writes data to a specific page in one transaction.
756    pub async fn page_plus_write(&mut self, addr: u8, data: &[u8]) -> Result<(), BUS::Error> {
757        self.block_write_cmd(addr, CommandCode::PagePlusWrite, data)
758            .await
759    }
760
761    /// Read KWH_IN (0x83) — 4-byte (32-bit) read via I2C write_read.
762    pub async fn read_kwh_in(&mut self, addr: u8) -> Result<u32, BUS::Error> {
763        let mut buf = [0u8; 4];
764        self.smbus
765            .write_read(addr, &[CommandCode::ReadKwhIn.code()], &mut buf)
766            .await?;
767        Ok(u32::from_le_bytes(buf))
768    }
769
770    /// Read KWH_OUT (0x84) — 4-byte (32-bit) read via I2C write_read.
771    pub async fn read_kwh_out(&mut self, addr: u8) -> Result<u32, BUS::Error> {
772        let mut buf = [0u8; 4];
773        self.smbus
774            .write_read(addr, &[CommandCode::ReadKwhOut.code()], &mut buf)
775            .await?;
776        Ok(u32::from_le_bytes(buf))
777    }
778
779    // =======================================================================
780    // Raw methods for manufacturer-specific codes
781    // =======================================================================
782
783    /// Read a byte from any command code.
784    pub async fn raw_read_byte(&mut self, addr: u8, code: u8) -> Result<u8, BUS::Error> {
785        self.smbus.read_byte(addr, code).await
786    }
787
788    /// Write a byte to any command code.
789    pub async fn raw_write_byte(&mut self, addr: u8, code: u8, data: u8) -> Result<(), BUS::Error> {
790        self.smbus.write_byte(addr, code, data).await
791    }
792
793    /// Read a word from any command code.
794    pub async fn raw_read_word(&mut self, addr: u8, code: u8) -> Result<u16, BUS::Error> {
795        self.smbus.read_word(addr, code).await
796    }
797
798    /// Write a word to any command code.
799    pub async fn raw_write_word(
800        &mut self,
801        addr: u8,
802        code: u8,
803        data: u16,
804    ) -> Result<(), BUS::Error> {
805        self.smbus.write_word(addr, code, data).await
806    }
807
808    /// Block read from any command code.
809    pub async fn raw_block_read(&mut self, addr: u8, code: u8) -> Result<Vec<u8, 32>, BUS::Error> {
810        self.smbus.block_read(addr, code).await
811    }
812
813    /// Block write to any command code.
814    pub async fn raw_block_write(
815        &mut self,
816        addr: u8,
817        code: u8,
818        data: &[u8],
819    ) -> Result<(), BUS::Error> {
820        self.smbus.block_write(addr, code, data).await
821    }
822
823    // =======================================================================
824    // Extended command protocol
825    // =======================================================================
826
827    /// Extended read byte — sends [prefix, ext_cmd] and reads 1 byte.
828    pub async fn extended_read_byte(
829        &mut self,
830        addr: u8,
831        prefix: u8,
832        ext_cmd: u8,
833    ) -> Result<u8, BUS::Error> {
834        let mut buf = [0u8; 1];
835        self.smbus
836            .write_read(addr, &[prefix, ext_cmd], &mut buf)
837            .await?;
838        Ok(buf[0])
839    }
840
841    /// Extended write byte — sends [prefix, ext_cmd, data].
842    pub async fn extended_write_byte(
843        &mut self,
844        addr: u8,
845        prefix: u8,
846        ext_cmd: u8,
847        data: u8,
848    ) -> Result<(), BUS::Error> {
849        self.smbus.write(addr, &[prefix, ext_cmd, data]).await
850    }
851
852    /// Extended read word — sends [prefix, ext_cmd] and reads 2 bytes (LE).
853    pub async fn extended_read_word(
854        &mut self,
855        addr: u8,
856        prefix: u8,
857        ext_cmd: u8,
858    ) -> Result<u16, BUS::Error> {
859        let mut buf = [0u8; 2];
860        self.smbus
861            .write_read(addr, &[prefix, ext_cmd], &mut buf)
862            .await?;
863        Ok(u16::from_le_bytes(buf))
864    }
865
866    /// Extended write word — sends [prefix, ext_cmd, lo, hi].
867    pub async fn extended_write_word(
868        &mut self,
869        addr: u8,
870        prefix: u8,
871        ext_cmd: u8,
872        data: u16,
873    ) -> Result<(), BUS::Error> {
874        let bytes = data.to_le_bytes();
875        self.smbus
876            .write(addr, &[prefix, ext_cmd, bytes[0], bytes[1]])
877            .await
878    }
879}