Skip to content

Commit ddea266

Browse files
authored
Merge pull request #122 from jessebraham/feature/usb-serial-jtag
Allow for flashing via the USB Serial JTAG peripheral
2 parents cd4ec7c + 6f88f36 commit ddea266

File tree

4 files changed

+104
-46
lines changed

4 files changed

+104
-46
lines changed

espflash/src/cli/mod.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,38 @@
1-
use self::clap::ConnectArgs;
21
/// CLI utilities shared between espflash and cargo-espflash
32
///
4-
/// No stability guaranties applies
3+
/// No stability guaranties apply
54
use config::Config;
65
use miette::{Result, WrapErr};
7-
use serialport::FlowControl;
6+
use serialport::{FlowControl, SerialPortType};
87

9-
use crate::cli::serial::get_serial_port;
10-
use crate::{error::Error, Flasher};
8+
use self::clap::ConnectArgs;
9+
use crate::{cli::serial::get_serial_port_info, error::Error, Flasher};
1110

1211
pub mod clap;
1312
pub mod config;
14-
mod line_endings;
1513
pub mod monitor;
14+
15+
mod line_endings;
1616
mod serial;
1717

1818
pub fn connect(matches: &ConnectArgs, config: &Config) -> Result<Flasher> {
19-
let port = get_serial_port(matches, config)?;
19+
let port_info = get_serial_port_info(matches, config)?;
2020

2121
// Attempt to open the serial port and set its initial baud rate.
22-
println!("Serial port: {}", port);
22+
println!("Serial port: {}", port_info.port_name);
2323
println!("Connecting...\n");
24-
let serial = serialport::new(&port, 115_200)
24+
let serial = serialport::new(&port_info.port_name, 115_200)
2525
.flow_control(FlowControl::None)
2626
.open()
2727
.map_err(Error::from)
28-
.wrap_err_with(|| format!("Failed to open serial port {}", port))?;
28+
.wrap_err_with(|| format!("Failed to open serial port {}", port_info.port_name))?;
29+
30+
// NOTE: since `get_serial_port_info` filters out all non-USB serial ports, we
31+
// can just pretend the remaining types don't exist here.
32+
let port_info = match port_info.port_type {
33+
SerialPortType::UsbPort(info) => info,
34+
_ => unreachable!(),
35+
};
2936

30-
Ok(Flasher::connect(serial, matches.speed)?)
37+
Ok(Flasher::connect(serial, port_info, matches.speed)?)
3138
}

espflash/src/cli/serial.rs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
1-
use super::config::Config;
21
use crossterm::style::Stylize;
32
use dialoguer::{theme::ColorfulTheme, Confirm, Select};
43
use miette::{IntoDiagnostic, Result};
54
use serialport::{available_ports, SerialPortInfo, SerialPortType};
65

7-
use super::clap::ConnectArgs;
8-
use crate::cli::config::UsbDevice;
9-
use crate::error::Error;
6+
use super::{clap::ConnectArgs, config::Config};
7+
use crate::{cli::config::UsbDevice, error::Error};
108

11-
pub fn get_serial_port(matches: &ConnectArgs, config: &Config) -> Result<String, Error> {
9+
pub fn get_serial_port_info(
10+
matches: &ConnectArgs,
11+
config: &Config,
12+
) -> Result<SerialPortInfo, Error> {
1213
// A serial port should be specified either as a command-line argument or in a
1314
// configuration file. In the case that both have been provided the command-line
1415
// argument takes precedence.
1516
//
1617
// Users may optionally specify the device's VID and PID in the configuration
17-
// file. If no VID/PID have been provided, the user will always be prompted to
18-
// select a serial device. If some VID/PID have been provided the user will be
19-
// prompted to select a serial device, unless there is only one found and its
20-
// VID/PID matches the configured values.
21-
if let Some(serial) = &matches.serial {
22-
Ok(serial.to_owned())
18+
// file. If no VID/PID has been provided, the user will always be prompted to
19+
// select a serial port. If some VID and PID were provided then the user will
20+
// also be prompted to select a port, unless there is only one found and its VID
21+
// and PID match the configured values.
22+
let ports = detect_usb_serial_ports().unwrap_or_default();
23+
24+
let maybe_port = if let Some(serial) = &matches.serial {
25+
find_serial_port(&ports, serial.to_owned())
2326
} else if let Some(serial) = &config.connection.serial {
24-
Ok(serial.to_owned())
25-
} else if let Ok(ports) = detect_usb_serial_ports() {
27+
find_serial_port(&ports, serial.to_owned())
28+
} else if !ports.is_empty() {
2629
let (port, matches) = select_serial_port(ports, config)?;
27-
match port.port_type {
30+
match &port.port_type {
2831
SerialPortType::UsbPort(usb_info) if !matches => {
2932
if Confirm::with_theme(&ColorfulTheme::default())
3033
.with_prompt("Remember this serial port for future use?")
@@ -43,20 +46,39 @@ pub fn get_serial_port(matches: &ConnectArgs, config: &Config) -> Result<String,
4346
}
4447
_ => {}
4548
}
46-
Ok(port.port_name)
49+
50+
Some(port)
51+
} else {
52+
None
53+
};
54+
55+
if let Some(port_info) = maybe_port {
56+
Ok(port_info)
4757
} else {
4858
Err(Error::NoSerial)
4959
}
5060
}
5161

52-
/// serialport's autodetect doesn't provide any port information when using musl linux
53-
/// we can do some manual parsing of sysfs to get the relevant bits without udev
62+
/// Given a vector of `SerialPortInfo` structs, attempt to find and return one
63+
/// whose `port_name` field matches the provided `name` argument.
64+
fn find_serial_port(ports: &[SerialPortInfo], name: String) -> Option<SerialPortInfo> {
65+
ports
66+
.iter()
67+
.find(|port| port.port_name == name)
68+
.map(|port| port.to_owned())
69+
}
70+
71+
/// serialport's autodetect doesn't provide any port information when using musl
72+
/// linux we can do some manual parsing of sysfs to get the relevant bits
73+
/// without udev
5474
#[cfg(all(target_os = "linux", target_env = "musl"))]
5575
fn detect_usb_serial_ports() -> Result<Vec<SerialPortInfo>> {
76+
use std::{
77+
fs::{read_link, read_to_string},
78+
path::PathBuf,
79+
};
80+
5681
use serialport::UsbPortInfo;
57-
use std::fs::read_link;
58-
use std::fs::read_to_string;
59-
use std::path::PathBuf;
6082

6183
let ports = available_ports().into_diagnostic()?;
6284
let ports = ports

espflash/src/connection.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66

77
use binread::{io::Cursor, BinRead, BinReaderExt};
88
use bytemuck::{Pod, Zeroable};
9-
use serialport::SerialPort;
9+
use serialport::{SerialPort, UsbPortInfo};
1010
use slip_codec::SlipDecoder;
1111

1212
use crate::{
@@ -15,6 +15,8 @@ use crate::{
1515
error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind},
1616
};
1717

18+
const USB_SERIAL_JTAG_PID: u16 = 0x1001;
19+
1820
#[derive(Debug, Copy, Clone, BinRead)]
1921
pub struct CommandResponse {
2022
pub resp: u8,
@@ -27,6 +29,7 @@ pub struct CommandResponse {
2729

2830
pub struct Connection {
2931
serial: Box<dyn SerialPort>,
32+
port_info: UsbPortInfo,
3033
decoder: SlipDecoder,
3134
}
3235

@@ -40,9 +43,10 @@ struct WriteRegParams {
4043
}
4144

4245
impl Connection {
43-
pub fn new(serial: Box<dyn SerialPort>) -> Self {
46+
pub fn new(serial: Box<dyn SerialPort>, port_info: UsbPortInfo) -> Self {
4447
Connection {
4548
serial,
49+
port_info,
4650
decoder: SlipDecoder::new(),
4751
}
4852
}
@@ -61,18 +65,39 @@ impl Connection {
6165
}
6266

6367
pub fn reset_to_flash(&mut self, extra_delay: bool) -> Result<(), Error> {
64-
self.serial.write_data_terminal_ready(false)?;
65-
self.serial.write_request_to_send(true)?;
68+
if self.port_info.pid == USB_SERIAL_JTAG_PID {
69+
self.serial.write_data_terminal_ready(false)?;
70+
self.serial.write_request_to_send(false)?;
6671

67-
sleep(Duration::from_millis(100));
72+
sleep(Duration::from_millis(100));
6873

69-
self.serial.write_data_terminal_ready(true)?;
70-
self.serial.write_request_to_send(false)?;
74+
self.serial.write_data_terminal_ready(true)?;
75+
self.serial.write_request_to_send(false)?;
7176

72-
let millis = if extra_delay { 500 } else { 50 };
73-
sleep(Duration::from_millis(millis));
77+
sleep(Duration::from_millis(100));
7478

75-
self.serial.write_data_terminal_ready(false)?;
79+
self.serial.write_request_to_send(true)?;
80+
self.serial.write_data_terminal_ready(false)?;
81+
self.serial.write_request_to_send(true)?;
82+
83+
sleep(Duration::from_millis(100));
84+
85+
self.serial.write_data_terminal_ready(false)?;
86+
self.serial.write_request_to_send(false)?;
87+
} else {
88+
self.serial.write_data_terminal_ready(false)?;
89+
self.serial.write_request_to_send(true)?;
90+
91+
sleep(Duration::from_millis(100));
92+
93+
self.serial.write_data_terminal_ready(true)?;
94+
self.serial.write_request_to_send(false)?;
95+
96+
let millis = if extra_delay { 500 } else { 50 };
97+
sleep(Duration::from_millis(millis));
98+
99+
self.serial.write_data_terminal_ready(false)?;
100+
}
76101

77102
Ok(())
78103
}

espflash/src/flasher.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::{borrow::Cow, thread::sleep};
22

33
use bytemuck::{Pod, Zeroable, __core::time::Duration};
4-
use serialport::SerialPort;
4+
use serialport::{SerialPort, UsbPortInfo};
55
use strum_macros::Display;
66

77
use crate::{
@@ -151,10 +151,14 @@ pub struct Flasher {
151151
}
152152

153153
impl Flasher {
154-
pub fn connect(serial: Box<dyn SerialPort>, speed: Option<u32>) -> Result<Self, Error> {
154+
pub fn connect(
155+
serial: Box<dyn SerialPort>,
156+
port_info: UsbPortInfo,
157+
speed: Option<u32>,
158+
) -> Result<Self, Error> {
155159
let mut flasher = Flasher {
156-
connection: Connection::new(serial), // default baud is always 115200
157-
chip: Chip::Esp8266, // dummy, set properly later
160+
connection: Connection::new(serial, port_info), // default baud is always 115200
161+
chip: Chip::Esp8266, // dummy, set properly later
158162
flash_size: FlashSize::Flash4Mb,
159163
spi_params: SpiAttachParams::default(), // may be set when trying to attach to flash
160164
};
@@ -165,7 +169,7 @@ impl Flasher {
165169

166170
if let Some(b) = speed {
167171
match flasher.chip {
168-
Chip::Esp8266 => (), /* Not available */
172+
Chip::Esp8266 => (), // Not available
169173
_ => {
170174
if b > 115_200 {
171175
println!("WARN setting baud rate higher than 115200 can cause issues.");

0 commit comments

Comments
 (0)