1use std::sync::Arc;
2
3use hidpp::{
4 channel::HidppChannel,
5 device::Device,
6 feature::CreatableFeature,
7 feature::FeatureType,
8 feature::adc_measurement::AdcMeasurementFeature,
9 feature::battery_status::BatteryStatusFeature,
10 feature::device_information::{
11 DeviceEntityFirmwareInfo, DeviceEntityType, DeviceInformationFeature,
12 },
13 feature::feature_set::FeatureSetFeature,
14 feature::unified_battery::UnifiedBatteryFeature,
15 protocol::v20::Hidpp20Error,
16};
17
18use crate::backend::HidBackend;
19use crate::channel::route::DeviceRoute;
20use crate::reprog_controls::{self, CidFlags, CidInfo, ReprogControlsV4};
21use crate::write::{HidppOperation, WriteError, classify_hidpp_error, open_feature, with_route};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct FeatureEntry {
27 pub id: u16,
29 pub version: u8,
31 pub typ: FeatureType,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct ReprogControlEntry {
41 pub cid: u16,
43 pub task_id: u16,
45 pub flags: CidFlags,
47}
48
49impl From<CidInfo> for ReprogControlEntry {
50 fn from(info: CidInfo) -> Self {
51 Self {
52 cid: info.cid.into(),
53 task_id: info.task_id.0,
54 flags: info.flags,
55 }
56 }
57}
58
59pub async fn dump_features(
65 backend: &dyn HidBackend,
66 route: &DeviceRoute,
67) -> Result<Vec<FeatureEntry>, WriteError> {
68 let index = route.device_index();
69 with_route(backend, route, move |channel| async move {
70 let mut device = Device::new(Arc::clone(&channel), index)
71 .await
72 .map_err(|_| WriteError::DeviceUnreachable { index })?;
73 let feature_set_info = device
77 .root()
78 .get_feature(FeatureSetFeature::ID)
79 .await
80 .map_err(|e| {
81 classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
82 })?
83 .ok_or(WriteError::FeatureUnsupported {
84 feature_hex: FeatureSetFeature::ID,
85 })?;
86 let feature_set = device.add_feature::<FeatureSetFeature>(feature_set_info.index);
87 let count = feature_set.count().await.map_err(|e| {
88 classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
89 })?;
90 let mut entries = Vec::with_capacity(usize::from(count));
91 for i in 0..=count {
92 let info = feature_set.get_feature(i).await.map_err(|e| {
93 classify_hidpp_error(e, HidppOperation::DumpFeatures, FeatureSetFeature::ID)
94 })?;
95 entries.push(FeatureEntry {
96 id: info.id,
97 version: info.version,
98 typ: info.typ,
99 });
100 }
101 Ok(entries)
102 })
103 .await
104}
105
106pub async fn dump_reprog_controls(
112 backend: &dyn HidBackend,
113 route: &DeviceRoute,
114) -> Result<Vec<ReprogControlEntry>, WriteError> {
115 let index = route.device_index();
116 with_route(backend, route, move |channel| async move {
117 let device = Device::new(Arc::clone(&channel), index)
118 .await
119 .map_err(|_| WriteError::DeviceUnreachable { index })?;
120 let info = device
121 .root()
122 .get_feature(reprog_controls::FEATURE_ID)
123 .await
124 .map_err(|e| {
125 classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
126 })?
127 .ok_or(WriteError::FeatureUnsupported {
128 feature_hex: reprog_controls::FEATURE_ID,
129 })?;
130 let rc = ReprogControlsV4::new(Arc::clone(&channel), index, info.index);
131 let count = rc.get_count().await.map_err(|e| {
132 classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
133 })?;
134 let mut entries = Vec::with_capacity(usize::from(count));
135 for i in 0..count {
136 let control = rc.get_cid_info(i).await.map_err(|e| {
137 classify_hidpp_error(e, HidppOperation::DumpFeatures, reprog_controls::FEATURE_ID)
138 })?;
139 entries.push(control.into());
140 }
141 Ok(entries)
142 })
143 .await
144}
145
146pub async fn read_battery_raw(
153 backend: &dyn HidBackend,
154 route: &DeviceRoute,
155) -> Result<String, WriteError> {
156 let index = route.device_index();
157 with_route(backend, route, move |channel| async move {
158 let mut device = Device::new(Arc::clone(&channel), index)
159 .await
160 .map_err(|_| WriteError::DeviceUnreachable { index })?;
161
162 match open_feature::<UnifiedBatteryFeature>(&mut device).await {
163 Ok(feature) => {
164 let info = feature
165 .get_battery_info()
166 .await
167 .map_err(|e| WriteError::Hidpp(format!("{e:?}")))?;
168 return Ok(format!(
169 "0x1004 UnifiedBattery: percentage={} level={:?} status={:?}",
170 info.charging_percentage, info.level, info.status
171 ));
172 }
173 Err(WriteError::FeatureUnsupported { .. }) => {}
174 Err(e) => return Err(e),
175 }
176
177 match open_feature::<BatteryStatusFeature>(&mut device).await {
178 Ok(feature) => {
179 let info = feature
180 .get_battery_level_status()
181 .await
182 .map_err(|e| WriteError::Hidpp(format!("{e:?}")))?;
183 return Ok(format!(
184 "0x1000 BatteryStatus: discharge_level={} next_level={} status={:?}",
185 info.discharge_level, info.next_level, info.status
186 ));
187 }
188 Err(WriteError::FeatureUnsupported { .. }) => {}
189 Err(e) => return Err(e),
190 }
191
192 match open_feature::<AdcMeasurementFeature>(&mut device).await {
193 Ok(feature) => {
194 let measurement = feature
195 .get_adc_measurement()
196 .await
197 .map_err(|e| WriteError::Hidpp(format!("{e:?}")))?;
198 return Ok(format!("0x1F20 AdcMeasurement: {measurement:?}"));
199 }
200 Err(WriteError::FeatureUnsupported { .. }) => {}
201 Err(e) => return Err(e),
202 }
203
204 Err(WriteError::FeatureUnsupported {
207 feature_hex: 0x1004,
208 })
209 })
210 .await
211}
212
213#[derive(Debug, Clone, PartialEq, Eq)]
222pub struct FirmwareEntityInfo {
223 pub kind: DeviceEntityType,
226 pub prefix: String,
228 pub number: u8,
230 pub revision: u8,
232 pub build: u16,
234 pub active: bool,
236 pub transport_pid: u16,
241 pub extra_version: [u8; 5],
245}
246
247impl From<DeviceEntityFirmwareInfo> for FirmwareEntityInfo {
248 fn from(info: DeviceEntityFirmwareInfo) -> Self {
249 Self {
250 kind: info.entity_type,
251 prefix: info.firmware_prefix,
252 number: info.firmware_number,
253 revision: info.revision,
254 build: info.build,
255 active: info.active,
256 transport_pid: info.transport_pid,
257 extra_version: info.extra_version,
258 }
259 }
260}
261
262#[derive(Debug, Clone, PartialEq, Eq)]
270pub enum FirmwareEntity {
271 Readable {
273 index: u8,
275 info: FirmwareEntityInfo,
277 },
278 Unreadable {
284 index: u8,
286 error: WriteError,
288 },
289}
290
291pub async fn dump_firmware_entities(
302 backend: &dyn HidBackend,
303 route: &DeviceRoute,
304) -> Result<Vec<FirmwareEntity>, WriteError> {
305 let index = route.device_index();
306 with_route(backend, route, move |channel| async move {
307 dump_firmware_entities_on_channel(&channel, index).await
308 })
309 .await
310}
311
312pub(crate) async fn dump_firmware_entities_on_channel(
315 channel: &Arc<HidppChannel>,
316 index: u8,
317) -> Result<Vec<FirmwareEntity>, WriteError> {
318 let mut device = Device::new(Arc::clone(channel), index)
319 .await
320 .map_err(|_| WriteError::DeviceUnreachable { index })?;
321 let feature = open_feature::<DeviceInformationFeature>(&mut device).await?;
322 let info = feature.get_device_info().await.map_err(|e| {
323 classify_hidpp_error(
324 e,
325 HidppOperation::DumpFeatures,
326 DeviceInformationFeature::ID,
327 )
328 })?;
329
330 let mut entries = Vec::with_capacity(usize::from(info.entity_count));
331 for entity in 0..info.entity_count {
332 match feature.get_fw_info(entity).await {
333 Ok(fw) => entries.push(FirmwareEntity::Readable {
334 index: entity,
335 info: fw.into(),
336 }),
337 Err(e @ (Hidpp20Error::Feature(_) | Hidpp20Error::UnsupportedResponse)) => {
342 entries.push(FirmwareEntity::Unreadable {
343 index: entity,
344 error: classify_hidpp_error(
345 e,
346 HidppOperation::DumpFeatures,
347 DeviceInformationFeature::ID,
348 ),
349 });
350 }
351 Err(e) => {
355 return Err(classify_hidpp_error(
356 e,
357 HidppOperation::DumpFeatures,
358 DeviceInformationFeature::ID,
359 ));
360 }
361 }
362 }
363 Ok(entries)
364}