Skip to content

Commit 183a7c5

Browse files
committed
refactor: introduce Transport trait
Signed-off-by: hagz0r <[email protected]>
1 parent 3635e52 commit 183a7c5

5 files changed

Lines changed: 127 additions & 40 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
**/*.dSYM/

src/cd321x.rs

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
* Copyright The Asahi Linux Contributors
55
*/
66

7+
use crate::transport::Transport;
78
use crate::{Error, Result};
8-
use i2cdev::{core::I2CDevice, linux::LinuxI2CDevice};
99
use log::{error, info};
1010
use std::{
11+
io,
1112
str::FromStr,
1213
thread,
1314
time::{Duration, Instant},
@@ -43,6 +44,7 @@ enum TpsMode {
4344

4445
impl FromStr for TpsMode {
4546
type Err = ();
47+
4648
fn from_str(input: &str) -> std::result::Result<TpsMode, ()> {
4749
match input {
4850
"APP " => Ok(TpsMode::App),
@@ -60,41 +62,24 @@ fn is_invalid_cmd(val: u32) -> bool {
6062
val == 0x444d4321
6163
}
6264

63-
pub(crate) struct Device {
64-
i2c: LinuxI2CDevice,
65+
pub(crate) struct Device<'a> {
66+
transport: &'a mut dyn Transport,
6567
key: Vec<u8>,
6668
}
6769

68-
/// Try to open the given I2C bus and slave address.
69-
/// Returns a configured LinuxI2CDevice on success.
70-
fn verify_i2c_device(bus: &str, slave_address: u16) -> Result<LinuxI2CDevice> {
71-
match LinuxI2CDevice::new(bus, slave_address) {
72-
Ok(dev) => {
73-
return Ok(dev);
74-
}
75-
Err(_) => {} // Fall through to attempt forced open
76-
}
70+
impl<'a> Device<'a> {
71+
pub(crate) fn new(transport: &'a mut dyn Transport, code: &str) -> Result<Self> {
72+
let mut key = code.as_bytes().to_vec();
73+
key.reverse();
7774

78-
info!("Safely opening failed ==> Forcefully opening device...");
79-
let forced = unsafe { LinuxI2CDevice::force_new(bus, slave_address) };
80-
match forced {
81-
Ok(dev) => Ok(dev),
82-
Err(_) => Err(Error::I2C),
83-
}
84-
}
75+
let mut device = Self { transport, key };
8576

86-
impl Device {
87-
pub(crate) fn new(bus: &str, address: u16, code: String) -> Result<Self> {
88-
let mut device = Self {
89-
i2c: verify_i2c_device(bus, address)?,
90-
key: code.into_bytes().into_iter().rev().collect::<Vec<u8>>(),
91-
};
9277
if device.get_mode()? != TpsMode::App {
9378
return Err(Error::TypecController);
9479
}
80+
9581
device.lock(device.key.clone().as_slice())?;
9682
device.dbma(true)?;
97-
9883
Ok(device)
9984
}
10085

@@ -109,7 +94,6 @@ impl Device {
10994
cmd_timeout: Duration,
11095
res_delay: Duration,
11196
) -> Result<()> {
112-
// First: Check CMD1 Register busy
11397
{
11498
let mut status_buf = [0u8; 4];
11599
self.read_block(TPS_REG_CMD1, &mut status_buf)?;
@@ -120,20 +104,18 @@ impl Device {
120104
}
121105
}
122106

123-
// Write input Data to DATA1
124107
if !in_data.is_empty() {
125108
self.write_block(TPS_REG_DATA1, in_data)?;
126109
}
127110

128-
// Write 4-byte command tag
129111
self.write_block(TPS_REG_CMD1, cmd_tag)?;
130112

131-
// Poll until CMD1 becomes zero or timeout
132113
let start = Instant::now();
133114
loop {
134115
let mut status_buf = [0u8; 4];
135116
self.read_block(TPS_REG_CMD1, &mut status_buf)?;
136117
let val = u32::from_le_bytes(status_buf);
118+
137119
if is_invalid_cmd(val) {
138120
info!("Invalid Command");
139121
return Err(Error::TypecController);
@@ -145,6 +127,7 @@ impl Device {
145127
return Err(Error::ControllerTimeout);
146128
}
147129
}
130+
148131
thread::sleep(res_delay);
149132
Ok(())
150133
}
@@ -155,23 +138,32 @@ impl Device {
155138
buf.push(reg);
156139
buf.push(size);
157140
buf.extend_from_slice(data);
158-
self.i2c.write(&buf).map_err(|_| Error::I2C)?;
141+
142+
self.transport.write(&buf).map_err(Error::Io)?;
159143
Ok(())
160144
}
161145

162-
fn read_block(&mut self, reg: u8, buf: &mut [u8]) -> Result<()> {
163-
self.i2c.write(&[reg]).map_err(|_| Error::I2C)?;
164-
let mut internal_buf = vec![0u8; buf.len() + 1];
165-
self.i2c.read(&mut internal_buf).map_err(|_| Error::I2C)?;
166-
buf.copy_from_slice(&internal_buf[1..=buf.len()]);
146+
fn read_block(&mut self, reg: u8, out: &mut [u8]) -> Result<()> {
147+
self.transport.write(&[reg]).map_err(Error::Io)?;
148+
149+
let need = out.len() + 1;
150+
let internal = self.transport.read(need).map_err(Error::Io)?;
151+
if internal.len() != need {
152+
return Err(Error::Io(io::Error::new(
153+
io::ErrorKind::UnexpectedEof,
154+
format!("read_block: expected {need} bytes, got {}", internal.len()),
155+
)));
156+
}
167157

158+
out.copy_from_slice(&internal[1..=out.len()]);
168159
Ok(())
169160
}
170161

171162
fn get_mode(&mut self) -> Result<TpsMode> {
172163
let mut buf = [0u8; 4];
173164
self.read_block(TPS_REG_MODE, &mut buf)?;
174-
let s = std::str::from_utf8(&buf).unwrap();
165+
166+
let s = std::str::from_utf8(&buf).map_err(Error::Utf8)?;
175167
let m = TpsMode::from_str(s).map_err(|_| Error::TypecController)?;
176168
Ok(m)
177169
}
@@ -196,11 +188,13 @@ impl Device {
196188
if self.get_mode()? != TpsMode::Dbma {
197189
return Err(Error::TypecController);
198190
}
191+
199192
let data = [
200193
vec![((sop as u8) << 4) | vdos.len() as u8],
201194
vdos.iter().flat_map(|val| val.to_le_bytes()).collect(),
202195
]
203196
.concat();
197+
204198
self.exec_cmd_with_timing(
205199
b"VDMs",
206200
&data,
@@ -250,6 +244,7 @@ impl Device {
250244
return Err(Error::ReconnectTimeout);
251245
}
252246
}
247+
253248
info!(" Connected");
254249
thread::sleep(RECONNECT_WAIT);
255250
self.serial()
@@ -260,6 +255,7 @@ impl Device {
260255
info!("Putting target into serial mode...");
261256
self.vdms(VdmSopType::SopStar, &vdos)?;
262257
info!("Putting local end into serial mode... ");
258+
263259
if self.get_mode()? != TpsMode::Dbma {
264260
return Err(Error::TypecController);
265261
}
@@ -273,7 +269,7 @@ impl Device {
273269
}
274270
}
275271

276-
impl Drop for Device {
272+
impl Drop for Device<'_> {
277273
fn drop(&mut self) {
278274
let lock: [u8; 4] = [0, 0, 0, 0];
279275
let _ = self.dbma(false);

src/main.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,20 @@
44
* Copyright The Asahi Linux Contributors
55
*/
66

7+
8+
#![cfg_attr(not(any(target_os = "linux", target_os = "android")), allow(dead_code, unused_imports))]
9+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
10+
fn main() {
11+
eprintln!("tuxvdmtool currently supports Linux only.");
12+
}
13+
714
pub mod cd321x;
15+
pub mod transport;
816

917
use env_logger::Env;
1018
use log::error;
1119
use std::{fs, process::ExitCode};
20+
use transport::i2c::I2cTransport;
1221

1322
#[derive(Debug)]
1423
#[allow(dead_code)]
@@ -45,7 +54,6 @@ fn vdmtool() -> Result<()> {
4554
clap::Command::new("serial").about("reboot the target and enter serial mode"),
4655
),
4756
)
48-
// dummy command to display help for "reboot serial"
4957
.subcommand(
5058
clap::Command::new("reboot serial").about("reboot the target and enter serial mode"),
5159
)
@@ -73,7 +81,10 @@ fn vdmtool() -> Result<()> {
7381
}
7482

7583
let code = device.to_uppercase();
76-
let mut device = cd321x::Device::new(matches.get_one::<String>("bus").unwrap(), addr, code)?;
84+
85+
let bus = matches.get_one::<String>("bus").unwrap();
86+
let mut transport = I2cTransport::new(bus.as_str(), addr)?;
87+
let mut device = cd321x::Device::new(&mut transport, &code)?;
7788

7889
match matches.subcommand() {
7990
Some(("dfu", _)) => {
@@ -100,6 +111,7 @@ fn vdmtool() -> Result<()> {
100111
Ok(())
101112
}
102113

114+
#[cfg(any(target_os = "linux", target_os = "android"))]
103115
fn main() -> ExitCode {
104116
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
105117

src/transport/i2c.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright The Asahi Linux Contributors
5+
*/
6+
7+
use crate::transport::Transport;
8+
use crate::{Error, Result};
9+
use std::io;
10+
11+
#[cfg(any(target_os = "linux", target_os = "android"))]
12+
use i2cdev::{core::I2CDevice, linux::LinuxI2CDevice};
13+
14+
#[cfg(any(target_os = "linux", target_os = "android"))]
15+
pub(crate) struct I2cTransport {
16+
dev: LinuxI2CDevice,
17+
}
18+
19+
#[cfg(any(target_os = "linux", target_os = "android"))]
20+
fn open_i2c(bus: &str, addr: u16) -> Result<LinuxI2CDevice> {
21+
if let Ok(dev) = LinuxI2CDevice::new(bus, addr) {
22+
return Ok(dev);
23+
}
24+
log::info!("Safely opening failed ==> Forcefully opening device...");
25+
unsafe { LinuxI2CDevice::force_new(bus, addr) }.map_err(|_| Error::I2C)
26+
}
27+
28+
#[cfg(any(target_os = "linux", target_os = "android"))]
29+
impl I2cTransport {
30+
pub(crate) fn new(bus: &str, addr: u16) -> Result<Self> {
31+
Ok(Self { dev: open_i2c(bus, addr)? })
32+
}
33+
}
34+
35+
#[cfg(any(target_os = "linux", target_os = "android"))]
36+
impl Transport for I2cTransport {
37+
fn write(&mut self, data: &[u8]) -> io::Result<()> {
38+
self.dev
39+
.write(data)
40+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
41+
}
42+
43+
fn read(&mut self, len: usize) -> io::Result<Vec<u8>> {
44+
let mut buf = vec![0u8; len];
45+
self.dev
46+
.read(&mut buf)
47+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
48+
Ok(buf)
49+
}
50+
}
51+
52+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
53+
pub(crate) struct I2cTransport;
54+
55+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
56+
impl I2cTransport {
57+
pub(crate) fn new(_bus: &str, _addr: u16) -> Result<Self> {
58+
Err(Error::FeatureMissing)
59+
}
60+
}
61+
62+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
63+
impl Transport for I2cTransport {
64+
fn write(&mut self, _data: &[u8]) -> io::Result<()> {
65+
Err(io::Error::new(io::ErrorKind::Unsupported, "i2c transport is linux-only"))
66+
}
67+
68+
fn read(&mut self, _len: usize) -> io::Result<Vec<u8>> {
69+
Err(io::Error::new(io::ErrorKind::Unsupported, "i2c transport is linux-only"))
70+
}
71+
}

src/transport/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub mod i2c;
2+
3+
pub trait Transport {
4+
fn write(&mut self, data: &[u8]) -> std::io::Result<()>;
5+
fn read(&mut self, len: usize) -> std::io::Result<Vec<u8>>;
6+
}

0 commit comments

Comments
 (0)