Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2931133

Browse files
committedSep 22, 2022
Add support for Raspberry Pi's internal UART peripherals
1 parent 812749e commit 2931133

File tree

14 files changed

+250
-48
lines changed

14 files changed

+250
-48
lines changed
 

‎.github/workflows/rpi.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
echo "CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc" >> $GITHUB_ENV
6868
6969
- name: Build binary
70-
run: cargo build --release --all --target=${{ inputs.target }}
70+
run: cargo build --release --all --target=${{ inputs.target }} --features=raspberry
7171

7272
- uses: papeloto/action-zip@v1
7373
with:

‎Cargo.lock‎

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎cargo-espflash/src/main.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ fn flash(
222222
if args.flash_args.monitor {
223223
let pid = flasher.get_usb_pid()?;
224224
monitor(
225-
flasher.into_serial(),
225+
flasher.into_interface(),
226226
Some(&elf_data),
227227
pid,
228228
args.connect_args.monitor_baud.unwrap_or(115_200),

‎espflash/Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ md5 = "0.7.0"
5353
miette = { version = "5.3.0", features = ["fancy"] }
5454
parse_int = "0.6.0"
5555
regex = "1.6.0"
56+
rppal = { version = "0.13", optional = true }
5657
serde = { version = "1.0.144", features = ["derive"] }
5758
serde-hex = "0.1.0"
5859
serde_json = "1.0.85"
@@ -70,3 +71,4 @@ xmas-elf = "0.8.0"
7071
[features]
7172
default = ["cli"]
7273
cli = ["clap", "crossterm", "dialoguer", "update-informer"]
74+
raspberry = ["rppal"]

‎espflash/README.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ OPTIONS:
3636
--bootloader <BOOTLOADER>
3737
Path to a binary (.bin) bootloader file
3838
39+
--dtr <DTR> [only available when built with the "raspberry" feature]
40+
DTR pin to use for the internal UART hardware. Uses BCM numbering
41+
3942
--erase-otadata
4043
Erase the OTADATA partition This is useful when using multiple OTA partitions and still
4144
wanting to be able to reflash via espflash
@@ -67,6 +70,9 @@ OPTIONS:
6770
--ram
6871
Load the application to RAM instead of Flash
6972
73+
--rts <RTS> [only available when built with the "raspberry" feature]
74+
RTS pin to use for the internal UART hardware. Uses BCM numbering
75+
7076
-s, --flash-size <SIZE>
7177
Flash size of the target [possible values: 256KB, 512KB, 1MB, 2MB, 4MB, 8MB, 16MB, 32MB,
7278
64MB, 128MB]
@@ -90,6 +96,12 @@ SUBCOMMANDS:
9096
write-bin-to-flash Writes a binary file to a specific address in the chip's flash
9197
```
9298

99+
## Compile-time features
100+
101+
- `raspberry`: enables configuring DTR and RTS GPIOs which are necessary to use a Raspberry Pi's
102+
internal UART peripherals. This feature is optional (external USB <-> UART converters work
103+
without it) and adds a dependency on [`rppal`](https://crates.io/crates/rppal).
104+
93105
## Configuration
94106

95107
You can also specify the serial port and/or expected VID/PID values by setting them in the configuration file. This file is in different locations depending on your operating system:

‎espflash/src/bin/espflash.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn flash(mut args: FlashArgs, config: &Config) -> Result<()> {
159159
let pid = flasher.get_usb_pid()?;
160160

161161
monitor(
162-
flasher.into_serial(),
162+
flasher.into_interface(),
163163
Some(&elf_data),
164164
pid,
165165
args.connect_args.monitor_baud.unwrap_or(115_200),

‎espflash/src/cli/config.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ pub struct Config {
1919
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
2020
pub struct Connection {
2121
pub serial: Option<String>,
22+
#[cfg(feature = "raspberry")]
23+
pub rts: Option<u8>,
24+
#[cfg(feature = "raspberry")]
25+
pub dtr: Option<u8>,
2226
}
2327

2428
#[derive(Debug, Deserialize, Serialize, Default, Clone)]

‎espflash/src/cli/mod.rs‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ use std::{
1111
use clap::Args;
1212
use config::Config;
1313
use miette::{IntoDiagnostic, Result, WrapErr};
14-
use serialport::{FlowControl, SerialPortType, UsbPortInfo};
14+
use serialport::{SerialPortType, UsbPortInfo};
1515
use strum::VariantNames;
1616

1717
use crate::{
1818
cli::{monitor::monitor, serial::get_serial_port_info},
1919
elf::ElfFirmwareImage,
20-
error::{Error, NoOtadataError},
20+
error::NoOtadataError,
2121
flasher::{FlashFrequency, FlashMode, FlashSize},
2222
image_format::ImageFormatType,
23+
interface::Interface,
2324
partition_table, Chip, Flasher, ImageFormatId, InvalidPartitionTable, MissingPartitionTable,
2425
PartitionTable,
2526
};
@@ -40,6 +41,17 @@ pub struct ConnectArgs {
4041
/// Serial port connected to target device
4142
#[clap(short = 'p', long)]
4243
pub port: Option<String>,
44+
45+
/// DTR pin to use for the internal UART hardware. Uses BCM numbering.
46+
#[cfg(feature = "raspberry")]
47+
#[cfg_attr(feature = "raspberry", clap(long))]
48+
pub dtr: Option<u8>,
49+
50+
/// RTS pin to use for the internal UART hardware. Uses BCM numbering.
51+
#[cfg(feature = "raspberry")]
52+
#[cfg_attr(feature = "raspberry", clap(long))]
53+
pub rts: Option<u8>,
54+
4355
/// Use RAM stub for loading
4456
#[clap(long)]
4557
pub use_stub: bool,
@@ -126,10 +138,8 @@ pub fn connect(args: &ConnectArgs, config: &Config) -> Result<Flasher> {
126138
// Attempt to open the serial port and set its initial baud rate.
127139
println!("Serial port: {}", port_info.port_name);
128140
println!("Connecting...\n");
129-
let serial = serialport::new(&port_info.port_name, 115_200)
130-
.flow_control(FlowControl::None)
131-
.open()
132-
.map_err(Error::from)
141+
142+
let interface = Interface::new(&port_info, args, config)
133143
.wrap_err_with(|| format!("Failed to open serial port {}", port_info.port_name))?;
134144

135145
// NOTE: since `get_serial_port_info` filters out all non-USB serial ports, we
@@ -150,7 +160,7 @@ pub fn connect(args: &ConnectArgs, config: &Config) -> Result<Flasher> {
150160
};
151161

152162
Ok(Flasher::connect(
153-
serial,
163+
interface,
154164
port_info,
155165
args.baud,
156166
args.use_stub,
@@ -169,7 +179,7 @@ pub fn serial_monitor(args: ConnectArgs, config: &Config) -> Result<()> {
169179
let pid = flasher.get_usb_pid()?;
170180

171181
monitor(
172-
flasher.into_serial(),
182+
flasher.into_interface(),
173183
None,
174184
pid,
175185
args.monitor_baud.unwrap_or(115_200),

‎espflash/src/cli/monitor.rs‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
io::{stdout, ErrorKind, Read, Write},
2+
io::{stdout, ErrorKind},
33
time::Duration,
44
};
55

@@ -9,9 +9,8 @@ use crossterm::{
99
};
1010
use espmonitor::{handle_serial, load_bin_context, SerialState};
1111
use miette::{IntoDiagnostic, Result};
12-
use serialport::SerialPort;
1312

14-
use crate::connection::reset_after_flash;
13+
use crate::{connection::reset_after_flash, interface::Interface};
1514

1615
/// Converts key events from crossterm into appropriate character/escape
1716
/// sequences which are then sent over the serial connection.
@@ -84,7 +83,7 @@ impl Drop for RawModeGuard {
8483
}
8584

8685
pub fn monitor(
87-
mut serial: Box<dyn SerialPort>,
86+
mut serial: Interface,
8887
elf: Option<&[u8]>,
8988
pid: u16,
9089
baud: u32,
@@ -96,8 +95,10 @@ pub fn monitor(
9695

9796
// Explicitly set the baud rate when starting the serial monitor, to allow using
9897
// different rates for flashing.
99-
serial.set_baud_rate(baud)?;
100-
serial.set_timeout(Duration::from_millis(5))?;
98+
serial.serial_port_mut().set_baud_rate(baud)?;
99+
serial
100+
.serial_port_mut()
101+
.set_timeout(Duration::from_millis(5))?;
101102

102103
let _raw_mode = RawModeGuard::new();
103104

@@ -109,12 +110,12 @@ pub fn monitor(
109110
serial_state = SerialState::new(symbols);
110111
} else {
111112
serial_state = SerialState::new(None);
112-
reset_after_flash(&mut *serial, pid)?;
113+
reset_after_flash(&mut serial, pid)?;
113114
}
114115

115116
let mut buff = [0; 1024];
116117
loop {
117-
let read_count = match serial.read(&mut buff) {
118+
let read_count = match serial.serial_port_mut().read(&mut buff) {
118119
Ok(count) => Ok(count),
119120
Err(e) if e.kind() == ErrorKind::TimedOut => Ok(0),
120121
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
@@ -131,16 +132,16 @@ pub fn monitor(
131132
match key.code {
132133
KeyCode::Char('c') => break,
133134
KeyCode::Char('r') => {
134-
reset_after_flash(&mut *serial, pid)?;
135+
reset_after_flash(&mut serial, pid)?;
135136
continue;
136137
}
137138
_ => {}
138139
}
139140
}
140141

141142
if let Some(bytes) = handle_key_event(key) {
142-
serial.write_all(&bytes)?;
143-
serial.flush()?;
143+
serial.serial_port_mut().write_all(&bytes)?;
144+
serial.serial_port_mut().flush()?;
144145
}
145146
}
146147
}

‎espflash/src/connection.rs‎

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
use std::{
2-
io::{BufWriter, Write},
3-
thread::sleep,
4-
time::Duration,
5-
};
1+
use std::{io::BufWriter, thread::sleep, time::Duration};
62

73
use binread::{io::Cursor, BinRead, BinReaderExt};
84
use bytemuck::{Pod, Zeroable};
9-
use serialport::{SerialPort, UsbPortInfo};
5+
use serialport::UsbPortInfo;
106
use slip_codec::SlipDecoder;
117

128
use crate::{
139
command::{Command, CommandType},
1410
encoder::SlipEncoder,
1511
error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind},
12+
interface::Interface,
1613
};
1714

1815
const DEFAULT_CONNECT_ATTEMPTS: usize = 7;
@@ -29,7 +26,7 @@ pub struct CommandResponse {
2926
}
3027

3128
pub struct Connection {
32-
serial: Box<dyn SerialPort>,
29+
serial: Interface,
3330
port_info: UsbPortInfo,
3431
decoder: SlipDecoder,
3532
}
@@ -44,7 +41,7 @@ struct WriteRegParams {
4441
}
4542

4643
impl Connection {
47-
pub fn new(serial: Box<dyn SerialPort>, port_info: UsbPortInfo) -> Self {
44+
pub fn new(serial: Interface, port_info: UsbPortInfo) -> Self {
4845
Connection {
4946
serial,
5047
port_info,
@@ -119,7 +116,7 @@ impl Connection {
119116

120117
pub fn reset(&mut self) -> Result<(), Error> {
121118
let pid = self.port_info.pid;
122-
Ok(reset_after_flash(&mut *self.serial, pid)?)
119+
Ok(reset_after_flash(&mut self.serial, pid)?)
123120
}
124121

125122
pub fn reset_to_flash(&mut self, extra_delay: bool) -> Result<(), Error> {
@@ -161,29 +158,36 @@ impl Connection {
161158
}
162159

163160
pub fn set_timeout(&mut self, timeout: Duration) -> Result<(), Error> {
164-
self.serial.set_timeout(timeout)?;
161+
self.serial.serial_port_mut().set_timeout(timeout)?;
165162
Ok(())
166163
}
167164

168165
pub fn set_baud(&mut self, speed: u32) -> Result<(), Error> {
169-
self.serial.set_baud_rate(speed)?;
166+
self.serial.serial_port_mut().set_baud_rate(speed)?;
170167

171168
Ok(())
172169
}
173170

174171
pub fn get_baud(&self) -> Result<u32, Error> {
175-
Ok(self.serial.baud_rate()?)
172+
Ok(self.serial.serial_port().baud_rate()?)
176173
}
177174

178175
pub fn with_timeout<T, F: FnMut(&mut Connection) -> Result<T, Error>>(
179176
&mut self,
180177
timeout: Duration,
181178
mut f: F,
182179
) -> Result<T, Error> {
183-
let old_timeout = self.serial.timeout();
184-
self.serial.set_timeout(timeout)?;
180+
let old_timeout = {
181+
let serial = self.serial.serial_port_mut();
182+
let old_timeout = serial.timeout();
183+
serial.set_timeout(timeout)?;
184+
old_timeout
185+
};
186+
185187
let result = f(self);
186-
self.serial.set_timeout(old_timeout)?;
188+
189+
self.serial.serial_port_mut().set_timeout(old_timeout)?;
190+
187191
result
188192
}
189193

@@ -199,8 +203,10 @@ impl Connection {
199203
}
200204

201205
pub fn write_command(&mut self, command: Command) -> Result<(), Error> {
202-
self.serial.clear(serialport::ClearBuffer::Input)?;
203-
let mut writer = BufWriter::new(&mut self.serial);
206+
let serial = self.serial.serial_port_mut();
207+
208+
serial.clear(serialport::ClearBuffer::Input)?;
209+
let mut writer = BufWriter::new(serial);
204210
let mut encoder = SlipEncoder::new(&mut writer)?;
205211
command.write(&mut encoder)?;
206212
encoder.finish()?;
@@ -261,11 +267,11 @@ impl Connection {
261267
}
262268

263269
pub fn flush(&mut self) -> Result<(), Error> {
264-
self.serial.flush()?;
270+
self.serial.serial_port_mut().flush()?;
265271
Ok(())
266272
}
267273

268-
pub fn into_serial(self) -> Box<dyn SerialPort> {
274+
pub fn into_interface(self) -> Interface {
269275
self.serial
270276
}
271277

@@ -274,7 +280,7 @@ impl Connection {
274280
}
275281
}
276282

277-
pub fn reset_after_flash(serial: &mut dyn SerialPort, pid: u16) -> Result<(), serialport::Error> {
283+
pub fn reset_after_flash(serial: &mut Interface, pid: u16) -> Result<(), serialport::Error> {
278284
sleep(Duration::from_millis(100));
279285

280286
if pid == USB_SERIAL_JTAG_PID {

‎espflash/src/error.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::{
1212
command::CommandType,
1313
flasher::{FlashFrequency, FlashMode, FlashSize},
1414
image_format::ImageFormatId,
15+
interface::SerialConfigError,
1516
partition_table::{CoreType, SubType, Type},
1617
Chip,
1718
};
@@ -90,6 +91,12 @@ https://github.com/espressif/esp32c3-direct-boot-example"
9091
help("Make sure the correct device is connected to the host system")
9192
)]
9293
SerialNotFound(String),
94+
#[error("Incorrect serial port configuration")]
95+
#[diagnostic(
96+
code(espflash::serial_config),
97+
help("Make sure you have specified the DTR signal if you are using an internal UART peripherial")
98+
)]
99+
SerialConfiguration(SerialConfigError),
93100
#[error("Canceled by user")]
94101
Canceled,
95102
#[error("The flash mode '{0}' is not valid")]
@@ -245,6 +252,12 @@ impl From<binread::Error> for Error {
245252
}
246253
}
247254

255+
impl From<SerialConfigError> for Error {
256+
fn from(err: SerialConfigError) -> Self {
257+
Self::SerialConfiguration(err)
258+
}
259+
}
260+
248261
#[derive(Copy, Clone, Debug, Error, Diagnostic)]
249262
#[allow(dead_code)]
250263
#[repr(u8)]

‎espflash/src/flasher.rs‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{borrow::Cow, str::FromStr, thread::sleep};
22

33
use bytemuck::{Pod, Zeroable, __core::time::Duration};
44
use log::debug;
5-
use serialport::{SerialPort, UsbPortInfo};
5+
use serialport::UsbPortInfo;
66
use strum_macros::{Display, EnumVariantNames};
77

88
use crate::{
@@ -12,6 +12,7 @@ use crate::{
1212
elf::{ElfFirmwareImage, FirmwareImage, RomSegment},
1313
error::{ConnectionError, FlashDetectError, ResultExt},
1414
image_format::ImageFormatId,
15+
interface::Interface,
1516
stubs::FlashStub,
1617
Error, PartitionTable,
1718
};
@@ -277,7 +278,7 @@ pub struct Flasher {
277278

278279
impl Flasher {
279280
pub fn connect(
280-
serial: Box<dyn SerialPort>,
281+
serial: Interface,
281282
port_info: UsbPortInfo,
282283
speed: Option<u32>,
283284
use_stub: bool,
@@ -709,10 +710,6 @@ impl Flasher {
709710
Ok(())
710711
}
711712

712-
pub fn into_serial(self) -> Box<dyn SerialPort> {
713-
self.connection.into_serial()
714-
}
715-
716713
pub fn get_usb_pid(&self) -> Result<u16, Error> {
717714
self.connection.get_usb_pid()
718715
}
@@ -728,6 +725,10 @@ impl Flasher {
728725
self.connection.flush()?;
729726
Ok(())
730727
}
728+
729+
pub fn into_interface(self) -> Interface {
730+
self.connection.into_interface()
731+
}
731732
}
732733

733734
pub(crate) fn get_erase_size(offset: usize, size: usize) -> usize {

‎espflash/src/interface.rs‎

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use std::io::Read;
2+
3+
use crate::{cli::ConnectArgs, Config, Error};
4+
use miette::{Context, Result};
5+
use serialport::{FlowControl, SerialPort, SerialPortInfo};
6+
7+
#[cfg(feature = "raspberry")]
8+
use rppal::gpio::{Gpio, OutputPin};
9+
10+
#[derive(thiserror::Error, Debug)]
11+
pub enum SerialConfigError {
12+
#[cfg(feature = "raspberry")]
13+
#[error("You need to specify both DTR and RTS pins when using an internal UART peripheral")]
14+
MissingDtrRtsForInternalUart,
15+
16+
#[cfg(feature = "raspberry")]
17+
#[error("GPIO {0} is not available")]
18+
GpioUnavailable(u8),
19+
}
20+
21+
/// Wrapper around SerialPort where platform-specific modifications can be implemented.
22+
pub struct Interface {
23+
pub serial_port: Box<dyn SerialPort>,
24+
#[cfg(feature = "raspberry")]
25+
pub dtr: Option<OutputPin>,
26+
#[cfg(feature = "raspberry")]
27+
pub rts: Option<OutputPin>,
28+
}
29+
30+
#[cfg(feature = "raspberry")]
31+
fn write_gpio(gpio: &mut OutputPin, level: bool) {
32+
if level {
33+
gpio.set_high();
34+
} else {
35+
gpio.set_low();
36+
}
37+
}
38+
39+
fn open_port(port_info: &SerialPortInfo) -> Result<Box<dyn SerialPort>> {
40+
serialport::new(&port_info.port_name, 115_200)
41+
.flow_control(FlowControl::None)
42+
.open()
43+
.map_err(Error::from)
44+
.wrap_err_with(|| format!("Failed to open serial port {}", port_info.port_name))
45+
}
46+
47+
impl Interface {
48+
#[cfg(feature = "raspberry")]
49+
pub(crate) fn new(
50+
port_info: &SerialPortInfo,
51+
args: &ConnectArgs,
52+
config: &Config,
53+
) -> Result<Self> {
54+
let rts_gpio = args.rts.or(config.connection.rts);
55+
let dtr_gpio = args.dtr.or(config.connection.dtr);
56+
57+
if port_info.port_type == serialport::SerialPortType::Unknown
58+
&& (dtr_gpio.is_none() || rts_gpio.is_none())
59+
{
60+
// Assume internal UART, which has no DTR pin and usually no RTS either.
61+
return Err(Error::from(SerialConfigError::MissingDtrRtsForInternalUart).into());
62+
}
63+
64+
let gpios = Gpio::new().unwrap();
65+
66+
let rts = if let Some(gpio) = rts_gpio {
67+
match gpios.get(gpio) {
68+
Ok(pin) => Some(pin.into_output()),
69+
Err(_) => return Err(Error::from(SerialConfigError::GpioUnavailable(gpio)).into()),
70+
}
71+
} else {
72+
None
73+
};
74+
75+
let dtr = if let Some(gpio) = dtr_gpio {
76+
match gpios.get(gpio) {
77+
Ok(pin) => Some(pin.into_output()),
78+
Err(_) => return Err(Error::from(SerialConfigError::GpioUnavailable(gpio)).into()),
79+
}
80+
} else {
81+
None
82+
};
83+
84+
Ok(Self {
85+
serial_port: open_port(port_info)?,
86+
rts,
87+
dtr,
88+
})
89+
}
90+
91+
#[cfg(not(feature = "raspberry"))]
92+
pub(crate) fn new(
93+
port_info: &SerialPortInfo,
94+
_args: &ConnectArgs,
95+
_config: &Config,
96+
) -> Result<Self> {
97+
Ok(Self {
98+
serial_port: open_port(port_info)?,
99+
})
100+
}
101+
102+
pub fn write_data_terminal_ready(&mut self, pin_state: bool) -> serialport::Result<()> {
103+
#[cfg(feature = "raspberry")]
104+
if let Some(gpio) = self.dtr.as_mut() {
105+
write_gpio(gpio, pin_state);
106+
return Ok(());
107+
}
108+
109+
self.serial_port.write_data_terminal_ready(pin_state)
110+
}
111+
112+
pub fn write_request_to_send(&mut self, pin_state: bool) -> serialport::Result<()> {
113+
#[cfg(feature = "raspberry")]
114+
if let Some(gpio) = self.rts.as_mut() {
115+
write_gpio(gpio, pin_state);
116+
return Ok(());
117+
}
118+
119+
self.serial_port.write_request_to_send(pin_state)
120+
}
121+
122+
pub fn into_serial(self) -> Box<dyn SerialPort> {
123+
self.serial_port
124+
}
125+
126+
pub fn serial_port(&self) -> &dyn SerialPort {
127+
self.serial_port.as_ref()
128+
}
129+
130+
pub fn serial_port_mut(&mut self) -> &mut dyn SerialPort {
131+
self.serial_port.as_mut()
132+
}
133+
}
134+
135+
// Note(dbuga): this impl is necessary because using `dyn SerialPort` as `dyn Read`
136+
// requires trait_upcasting which isn't stable yet.
137+
impl Read for Interface {
138+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
139+
self.serial_port.read(buf)
140+
}
141+
}

‎espflash/src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod error;
1919
pub mod flash_target;
2020
pub mod flasher;
2121
pub mod image_format;
22+
pub mod interface;
2223
pub mod partition_table;
2324
pub mod stubs;
2425

0 commit comments

Comments
 (0)
Please sign in to comment.