Skip to content
This repository has been archived by the owner on Mar 26, 2024. It is now read-only.

Add a register method and use it when connecting #26

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ enum Command {

ReadConfig = 0xF2,
WriteConfig = 0xF3,

Register = 0x09,
}

#[repr(u8)]
#[allow(dead_code)]
enum RegisterCommand {
Register = 0x64,
Unregister = 0x65,
}

#[repr(u8)]
Expand Down Expand Up @@ -208,6 +217,15 @@ impl SwoStatus {
}
}

#[derive(Default, Debug)]
pub struct Connection {
pub handle: u16,
pub pid: u32,
pub hid: Option<std::net::Ipv4Addr>,
pub iid: u8,
pub cid: u8,
}

/// A handle to a J-Link USB device.
///
/// This is the main interface type of this library. There are multiple ways of obtaining an
Expand All @@ -220,6 +238,7 @@ impl SwoStatus {
/// [`UsbDeviceInfo`]. Also see [`scan_usb`].
pub struct JayLink {
handle: rusb::DeviceHandle<rusb::GlobalContext>,
conn: Option<Connection>,

read_ep: u8,
write_ep: u8,
Expand Down Expand Up @@ -431,11 +450,20 @@ impl JayLink {
caps: Capabilities::from_raw_legacy(0), // dummy value
interface: Interface::Spi, // dummy value, must not be JTAG
interfaces: Interfaces::from_bits_warn(0), // dummy value
conn: None,
handle,
};
this.fill_capabilities()?;
this.fill_interfaces()?;

let mut conn = Connection::default();
if this.caps.contains(Capability::Register) {
debug!("Registering connection");
this.register(&mut conn)?;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the device runs out of connections? Is that something that can happen? Will it reject new connections from being registered or will older ones be dropped automatically?

(this is something that could easily happen when an app either calls process::exit or is killed via Ctrl+C)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On my old-clone-firmware-whatever device, it never runs out of connections. It always has four, and newer ones just overwrite older ones, it's like a 4-size ring buffer.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, good. I was going to test this on my (original) J-Link but noticed that the list example is failing with <USB error while reading from device: Overflow>, so it looks like this doesn't work anymore with some hardware? (this is a J-Link Base Compact version 10.1)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm. Where is it failing? Is that after receiving the reply for registration? If so, can you dump the lengths (after // Receive connection table) and/or the whole received buf?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just the read of 76 Bytes failing:

[2021-07-25T12:15:04Z DEBUG jaylink] Registering connection
[2021-07-25T12:15:04Z TRACE jaylink] write 14 bytes: [9, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[2021-07-25T12:15:04Z TRACE jaylink] reading 76 bytes
Bus 001 Address 029 Port 003: VID=1366 PID=0105 – <USB error while reading from device: Overflow>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, so it is an actual USB level error? hmm. Does OpenOCD work with this device?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should work. I believe we need to change how reads/writes work, because libjaylink seems to use an extra buffer, while we directly call into libusb (which will refuse to read more than 76 Bytes into the 76 Byte buffer). You might not be hitting this problem because the clone firmware always returns a fixed-length response here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the FW I have always returns 76. Hmm. I guess we could use an arbitrary big buffer just for this? ¯\_(ツ)_/¯

debug!("Got handle {}", conn.handle);
this.conn = Some(conn);
}

Ok(this)
}

Expand Down Expand Up @@ -882,6 +910,59 @@ impl JayLink {
Ok(())
}

/// Registers a connection on the device.
/// Updates the Connection with the returned handle number.
///
/// This requires the [`Register`] capability.
///
/// **Note**: This may be **required** on some devices for SWD to work at all.
///
/// [`Register`]: Capability::Register
pub fn register(&self, conn: &mut Connection) -> Result<()> {
let handle = self.raw_register(conn, RegisterCommand::Register)?;
conn.handle = handle;
Ok(())
}

/// Unregisters a connection on the device.
///
/// This requires the [`Register`] capability.
///
/// [`Register`]: Capability::Register
pub fn unregister(&self, conn: &Connection) -> Result<()> {
self.raw_register(conn, RegisterCommand::Unregister)
.map(|_| ())
}

fn raw_register(&self, conn: &Connection, cmd: RegisterCommand) -> Result<u16> {
self.require_capability(Capability::Register)?;
let mut buf = [0; 14];
buf[0] = Command::Register as u8;
buf[1] = cmd as u8;
buf[2..6].copy_from_slice(&conn.pid.to_le_bytes());
buf[6..10].copy_from_slice(
&u32::from(conn.hid.unwrap_or(std::net::Ipv4Addr::UNSPECIFIED)).to_be_bytes(),
);
buf[10] = conn.iid;
buf[11] = conn.cid;
buf[12..14].copy_from_slice(&conn.handle.to_le_bytes());
self.write_cmd(&buf)?;

// Receive connection table
let mut buf = [0; 76];
self.read(&mut buf)?;
let new_handle = u16::from_le_bytes([buf[0], buf[1]]);
let num_entries = u16::from_le_bytes([buf[2], buf[3]]);
let entry_size = u16::from_le_bytes([buf[4], buf[5]]);
let info_size = u16::from_le_bytes([buf[6], buf[7]]);
let all_size = 8 + (num_entries * entry_size) + info_size;
if all_size > 76 {
let mut extra_buf = vec![0; (all_size - 76) as usize];
self.read(&mut extra_buf)?;
}
Ok(new_handle)
}

/// Performs a JTAG I/O operation.
///
/// This will shift out data on `TMS` (pin 7) and `TDI` (pin 5), while reading data shifted
Expand Down Expand Up @@ -1196,6 +1277,14 @@ impl JayLink {
}
}

impl Drop for JayLink {
fn drop(&mut self) {
if let Some(ref conn) = self.conn {
let _ = self.unregister(conn);
}
}
}

impl fmt::Debug for JayLink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JayLink")
Expand Down