1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Realtek firmware tools
16 
17 use crate::{Download, Source};
18 use anyhow::anyhow;
19 use bumble::wrapper::{
20     drivers::rtk::{Driver, DriverInfo, Firmware},
21     host::{DriverFactory, Host},
22     transport::Transport,
23 };
24 use owo_colors::{colors::css, OwoColorize};
25 use pyo3::PyResult;
26 use std::{fs, path};
27 
download(dl: Download) -> PyResult<()>28 pub(crate) async fn download(dl: Download) -> PyResult<()> {
29     let data_dir = dl
30         .output_dir
31         .or_else(|| {
32             directories::ProjectDirs::from("com", "google", "bumble")
33                 .map(|pd| pd.data_local_dir().join("firmware").join("realtek"))
34         })
35         .unwrap_or_else(|| {
36             eprintln!("Could not determine standard data directory");
37             path::PathBuf::from(".")
38         });
39     fs::create_dir_all(&data_dir)?;
40 
41     let (base_url, uses_bin_suffix) = match dl.source {
42         Source::LinuxKernel => ("https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/rtl_bt", true),
43         Source::RealtekOpensource => ("https://github.com/Realtek-OpenSource/android_hardware_realtek/raw/rtk1395/bt/rtkbt/Firmware/BT", false),
44         Source::LinuxFromScratch => ("https://anduin.linuxfromscratch.org/sources/linux-firmware/rtl_bt", true),
45     };
46 
47     println!("Downloading");
48     println!("{} {}", "FROM:".green(), base_url);
49     println!("{} {}", "TO:".green(), data_dir.to_string_lossy());
50 
51     let url_for_file = |file_name: &str| {
52         let url_suffix = if uses_bin_suffix {
53             file_name
54         } else {
55             file_name.trim_end_matches(".bin")
56         };
57 
58         let mut url = base_url.to_string();
59         url.push('/');
60         url.push_str(url_suffix);
61         url
62     };
63 
64     let to_download = if let Some(single) = dl.single {
65         vec![(
66             format!("{single}_fw.bin"),
67             Some(format!("{single}_config.bin")),
68             false,
69         )]
70     } else {
71         DriverInfo::all_drivers()?
72             .iter()
73             .map(|di| Ok((di.firmware_name()?, di.config_name()?, di.config_needed()?)))
74             .collect::<PyResult<Vec<_>>>()?
75     };
76 
77     let client = SimpleClient::new();
78 
79     for (fw_filename, config_filename, config_needed) in to_download {
80         println!("{}", "---".yellow());
81         let fw_path = data_dir.join(&fw_filename);
82         let config_path = config_filename.as_ref().map(|f| data_dir.join(f));
83 
84         if fw_path.exists() && !dl.overwrite {
85             println!(
86                 "{}",
87                 format!("{} already exists, skipping", fw_path.to_string_lossy())
88                     .fg::<css::Orange>()
89             );
90             continue;
91         }
92         if let Some(cp) = config_path.as_ref() {
93             if cp.exists() && !dl.overwrite {
94                 println!(
95                     "{}",
96                     format!("{} already exists, skipping", cp.to_string_lossy())
97                         .fg::<css::Orange>()
98                 );
99                 continue;
100             }
101         }
102 
103         let fw_contents = match client.get(&url_for_file(&fw_filename)).await {
104             Ok(data) => {
105                 println!("Downloaded {}: {} bytes", fw_filename, data.len());
106                 data
107             }
108             Err(e) => {
109                 eprintln!(
110                     "{} {} {:?}",
111                     "Failed to download".red(),
112                     fw_filename.red(),
113                     e
114                 );
115                 continue;
116             }
117         };
118 
119         let config_contents = if let Some(cn) = &config_filename {
120             match client.get(&url_for_file(cn)).await {
121                 Ok(data) => {
122                     println!("Downloaded {}: {} bytes", cn, data.len());
123                     Some(data)
124                 }
125                 Err(e) => {
126                     if config_needed {
127                         eprintln!("{} {} {:?}", "Failed to download".red(), cn.red(), e);
128                         continue;
129                     } else {
130                         eprintln!(
131                             "{}",
132                             format!("No config available as {cn}").fg::<css::Orange>()
133                         );
134                         None
135                     }
136                 }
137             }
138         } else {
139             None
140         };
141 
142         fs::write(&fw_path, &fw_contents)?;
143         if !dl.no_parse && config_filename.is_some() {
144             println!("{} {}", "Parsing:".cyan(), &fw_filename);
145             match Firmware::parse(&fw_contents).map_err(|e| anyhow!("Parse error: {:?}", e)) {
146                 Ok(fw) => dump_firmware_desc(&fw),
147                 Err(e) => {
148                     eprintln!(
149                         "{} {:?}",
150                         "Could not parse firmware:".fg::<css::Orange>(),
151                         e
152                     );
153                 }
154             }
155         }
156         if let Some((cp, cd)) = config_path
157             .as_ref()
158             .and_then(|p| config_contents.map(|c| (p, c)))
159         {
160             fs::write(cp, &cd)?;
161         }
162     }
163 
164     Ok(())
165 }
166 
parse(firmware_path: &path::Path) -> PyResult<()>167 pub(crate) fn parse(firmware_path: &path::Path) -> PyResult<()> {
168     let contents = fs::read(firmware_path)?;
169     let fw = Firmware::parse(&contents)
170         // squish the error into a string to avoid the error type requiring that the input be
171         // 'static
172         .map_err(|e| anyhow!("Parse error: {:?}", e))?;
173 
174     dump_firmware_desc(&fw);
175 
176     Ok(())
177 }
178 
info(transport: &str, force: bool) -> PyResult<()>179 pub(crate) async fn info(transport: &str, force: bool) -> PyResult<()> {
180     let transport = Transport::open(transport).await?;
181 
182     let mut host = Host::new(transport.source()?, transport.sink()?).await?;
183     host.reset(DriverFactory::None).await?;
184 
185     if !force && !Driver::check(&host).await? {
186         println!("USB device not supported by this RTK driver");
187     } else if let Some(driver_info) = Driver::driver_info_for_host(&host).await? {
188         println!("Driver:");
189         println!("  {:10} {:04X}", "ROM:", driver_info.rom()?);
190         println!("  {:10} {}", "Firmware:", driver_info.firmware_name()?);
191         println!(
192             "  {:10} {}",
193             "Config:",
194             driver_info.config_name()?.unwrap_or_default()
195         );
196     } else {
197         println!("Firmware already loaded or no supported driver for this device.")
198     }
199 
200     Ok(())
201 }
202 
load(transport: &str, force: bool) -> PyResult<()>203 pub(crate) async fn load(transport: &str, force: bool) -> PyResult<()> {
204     let transport = Transport::open(transport).await?;
205 
206     let mut host = Host::new(transport.source()?, transport.sink()?).await?;
207     host.reset(DriverFactory::None).await?;
208 
209     match Driver::for_host(&host, force).await? {
210         None => {
211             eprintln!("Firmware already loaded or no supported driver for this device.");
212         }
213         Some(mut d) => d.download_firmware().await?,
214     };
215 
216     Ok(())
217 }
218 
drop(transport: &str) -> PyResult<()>219 pub(crate) async fn drop(transport: &str) -> PyResult<()> {
220     let transport = Transport::open(transport).await?;
221 
222     let mut host = Host::new(transport.source()?, transport.sink()?).await?;
223     host.reset(DriverFactory::None).await?;
224 
225     Driver::drop_firmware(&mut host).await?;
226 
227     Ok(())
228 }
229 
dump_firmware_desc(fw: &Firmware)230 fn dump_firmware_desc(fw: &Firmware) {
231     println!(
232         "Firmware: version=0x{:08X} project_id=0x{:04X}",
233         fw.version(),
234         fw.project_id()
235     );
236     for p in fw.patches() {
237         println!(
238             "  Patch: chip_id=0x{:04X}, {} bytes, SVN Version={:08X}",
239             p.chip_id(),
240             p.contents().len(),
241             p.svn_version()
242         )
243     }
244 }
245 
246 struct SimpleClient {
247     client: reqwest::Client,
248 }
249 
250 impl SimpleClient {
new() -> Self251     fn new() -> Self {
252         Self {
253             client: reqwest::Client::new(),
254         }
255     }
256 
get(&self, url: &str) -> anyhow::Result<Vec<u8>>257     async fn get(&self, url: &str) -> anyhow::Result<Vec<u8>> {
258         let resp = self.client.get(url).send().await?;
259         if !resp.status().is_success() {
260             return Err(anyhow!("Bad status: {}", resp.status()));
261         }
262         let bytes = resp.bytes().await?;
263         Ok(bytes.as_ref().to_vec())
264     }
265 }
266