1pub use crate::wire::{DfuDevice, DfuOutput};
36
37#[derive(Debug, Clone, Default)]
42pub struct DfuOptions {
43 pub vid_pid: Option<String>,
45 pub serial: Option<String>,
47 pub alt: Option<u32>,
49 pub dfuse_address: Option<String>,
52 pub reset: bool,
54}
55
56pub(crate) mod ops {
57 use std::time::Duration;
58
59 use serde_json::{json, Value};
60
61 use super::DfuOptions;
62 use crate::error::Result;
63 use crate::nets::debug::base64_encode;
64 use crate::wire::{
65 box_command, dfu_unsupported, map_route_missing, value_as, value_list_field, DfuDevice,
66 DfuOutput, Op, Timeout,
67 };
68
69 const PATH: &str = "/usb/dfu";
70
71 const LIST_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(30));
74 const RUN_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(180));
75
76 fn params(opts: &DfuOptions) -> Value {
77 let mut params = json!({});
78 if let Some(vid_pid) = &opts.vid_pid {
79 params["vid_pid"] = json!(vid_pid);
80 }
81 if let Some(serial) = &opts.serial {
82 params["serial"] = json!(serial);
83 }
84 if let Some(alt) = opts.alt {
85 params["alt"] = json!(alt);
86 }
87 if let Some(addr) = &opts.dfuse_address {
88 params["dfuse_address"] = json!(addr);
89 }
90 if opts.reset {
91 params["reset"] = json!(true);
92 }
93 params
94 }
95
96 pub(crate) fn list() -> Op<Vec<DfuDevice>> {
97 Op {
98 req: box_command(PATH, "list", json!({}), LIST_TIMEOUT),
99 parse: |resp| value_list_field(resp, "devices"),
100 }
101 }
102
103 pub(crate) fn download(firmware: &[u8], opts: &DfuOptions) -> Op<DfuOutput> {
104 let mut p = params(opts);
105 p["firmware"] = json!(base64_encode(firmware));
106 Op {
107 req: box_command(PATH, "download", p, RUN_TIMEOUT),
108 parse: value_as::<DfuOutput>,
109 }
110 }
111
112 pub(crate) fn detach(opts: &DfuOptions) -> Op<DfuOutput> {
113 Op {
114 req: box_command(PATH, "detach", params(opts), RUN_TIMEOUT),
115 parse: value_as::<DfuOutput>,
116 }
117 }
118
119 pub(crate) fn compat<T>(result: Result<T>) -> Result<T> {
122 result.map_err(|e| map_route_missing(e, dfu_unsupported))
123 }
124}
125
126#[cfg(feature = "blocking")]
128#[derive(Clone)]
129pub struct Dfu<'a> {
130 pub(crate) client: &'a crate::client::LagerBox,
131}
132
133#[cfg(feature = "blocking")]
134impl Dfu<'_> {
135 pub fn list(&self) -> crate::Result<Vec<DfuDevice>> {
137 ops::compat(self.client.run(ops::list()))
138 }
139
140 pub fn download(&self, firmware: &[u8], opts: &DfuOptions) -> crate::Result<DfuOutput> {
143 ops::compat(self.client.run(ops::download(firmware, opts)))
144 }
145
146 pub fn detach(&self, opts: &DfuOptions) -> crate::Result<DfuOutput> {
148 ops::compat(self.client.run(ops::detach(opts)))
149 }
150}
151
152#[cfg(feature = "async")]
154#[derive(Clone)]
155pub struct AsyncDfu<'a> {
156 pub(crate) client: &'a crate::async_client::AsyncLagerBox,
157}
158
159#[cfg(feature = "async")]
160impl AsyncDfu<'_> {
161 pub async fn list(&self) -> crate::Result<Vec<DfuDevice>> {
163 ops::compat(self.client.run(ops::list()).await)
164 }
165
166 pub async fn download(&self, firmware: &[u8], opts: &DfuOptions) -> crate::Result<DfuOutput> {
169 ops::compat(self.client.run(ops::download(firmware, opts)).await)
170 }
171
172 pub async fn detach(&self, opts: &DfuOptions) -> crate::Result<DfuOutput> {
174 ops::compat(self.client.run(ops::detach(opts)).await)
175 }
176}