Skip to content

Commit 24147e1

Browse files
committed
unlinking pending transactions
1 parent d4c9535 commit 24147e1

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

src/main.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use dispatch2::dispatch_main;
77
use log::debug;
88
use simple_logger::SimpleLogger;
99
use std::io::{self, Write};
10+
use tokio::signal;
1011
use usbip::UsbIpClient;
1112

1213
mod controller_interface;
@@ -266,6 +267,9 @@ fn spawn_polling_task(
266267
rt: &tokio::runtime::Runtime,
267268
usbip_client: ForceableSend<Arc<UnsafeCell<UsbIpClient>>>,
268269
) {
270+
let client_for_signal = ForceableSend(Arc::clone(&usbip_client.0));
271+
272+
// Spawn the polling task
269273
rt.spawn(async move {
270274
debug!("Controller interface initialized successfully.");
271275
let cl = usbip_client;
@@ -279,4 +283,38 @@ fn spawn_polling_task(
279283
}
280284
}
281285
});
286+
287+
// Spawn the signal handler task
288+
rt.spawn(async move {
289+
let cl = client_for_signal;
290+
291+
let ctrl_c = signal::ctrl_c();
292+
293+
let terminate = async {
294+
signal::unix::signal(signal::unix::SignalKind::terminate())
295+
.expect("Failed to install SIGTERM handler")
296+
.recv()
297+
.await;
298+
};
299+
300+
tokio::select! {
301+
_ = ctrl_c => {
302+
debug!("Received SIGINT, shutting down...");
303+
}
304+
_ = terminate => {
305+
debug!("Received SIGTERM, shutting down...");
306+
}
307+
}
308+
309+
// Unlink all pending transactions before exiting
310+
eprintln!("\nShutting down, unlinking pending transactions...");
311+
unsafe {
312+
let client_ref = &mut *cl.0.get();
313+
if let Err(e) = client_ref.unlink_all_pending().await {
314+
eprintln!("Error unlinking pending transactions: {}", e);
315+
}
316+
}
317+
318+
process::exit(0);
319+
});
282320
}

src/usbip.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,14 @@ impl UsbIpCommand {
191191
#[derive(Debug, Clone, Copy)]
192192
enum UsbIpCmdType {
193193
CmdSubmit = 0x00000001,
194+
CmdUnlink = 0x00000002,
194195
}
195196

196197
impl UsbIpCmdType {
197198
fn name(self) -> &'static str {
198199
match self {
199200
Self::CmdSubmit => "USBIP_CMD_SUBMIT",
201+
Self::CmdUnlink => "USBIP_CMD_UNLINK",
200202
}
201203
}
202204
}
@@ -328,6 +330,34 @@ impl UsbCommandSubmit {
328330
}
329331
}
330332

333+
#[derive(Debug, Clone)]
334+
pub struct UsbCommandUnlink {
335+
header: UsbCommandHeader,
336+
seqnum_to_unlink: u32,
337+
}
338+
339+
impl UsbCommandUnlink {
340+
pub fn new(header: UsbCommandHeader, seqnum_to_unlink: u32) -> Self {
341+
Self {
342+
header,
343+
seqnum_to_unlink,
344+
}
345+
}
346+
347+
pub fn to_bytes(&self) -> BytesMut {
348+
let mut buf = BytesMut::with_capacity(USB_COMMAND_HEADER_SIZE + 24);
349+
buf.put(&self.header.to_bytes()[..]);
350+
buf.put_u32(self.seqnum_to_unlink);
351+
// Padding to match USBIP_CMD_UNLINK structure (24 bytes after header)
352+
buf.put_u32(0); // padding
353+
buf.put_u32(0); // padding
354+
buf.put_u32(0); // padding
355+
buf.put_u32(0); // padding
356+
buf.put_u32(0); // padding
357+
buf
358+
}
359+
}
360+
331361
pub struct UsbReturnSubmit {
332362
header: UsbCommandHeader,
333363
status: u32,
@@ -519,6 +549,58 @@ impl UsbIpClient {
519549
Ok(response)
520550
}
521551

552+
/// Send an unlink command to cancel a pending transaction
553+
pub async fn cmd_unlink(&mut self, seqnum_to_unlink: u32) -> UsbIpResult<()> {
554+
self.seqnum = self.seqnum.wrapping_add(1);
555+
556+
let device = self.get_imported_device()?;
557+
let header = UsbCommandHeader::new(
558+
UsbIpCmdType::CmdUnlink as u32,
559+
self.seqnum,
560+
(device.busnum << 16) | device.devnum,
561+
0, // direction doesn't matter for unlink
562+
0, // endpoint doesn't matter for unlink
563+
);
564+
565+
let cmd = UsbCommandUnlink::new(header, seqnum_to_unlink);
566+
let stream = self.get_stream_mut()?;
567+
stream.write_all(&cmd.to_bytes()).await?;
568+
debug!(
569+
"Sent {} request for seqnum {}",
570+
UsbIpCmdType::CmdUnlink.name(),
571+
seqnum_to_unlink
572+
);
573+
574+
Ok(())
575+
}
576+
577+
/// Unlink all pending transactions - call this before shutdown
578+
pub async fn unlink_all_pending(&mut self) -> UsbIpResult<()> {
579+
let pending_seqnums: Vec<u32> = {
580+
let pending = self.pending.lock().await;
581+
pending.keys().copied().collect()
582+
};
583+
584+
if pending_seqnums.is_empty() {
585+
debug!("No pending transactions to unlink");
586+
return Ok(());
587+
}
588+
589+
debug!(
590+
"Unlinking {} pending transactions: {:?}",
591+
pending_seqnums.len(),
592+
pending_seqnums
593+
);
594+
595+
for seqnum in pending_seqnums {
596+
if let Err(e) = self.cmd_unlink(seqnum).await {
597+
debug!("Failed to unlink seqnum {}: {}", seqnum, e);
598+
}
599+
}
600+
601+
Ok(())
602+
}
603+
522604
pub async fn poll(&mut self) -> UsbIpResult<()> {
523605
let stream = match self.stream.as_mut() {
524606
Some(s) => s,
@@ -536,6 +618,19 @@ impl UsbIpClient {
536618
let header = UsbCommandHeader::from_stream(stream).await?;
537619
let seqnum = header.seqnum();
538620

621+
// Handle RET_UNLINK (0x04) separately - just consume the response
622+
if header.command == 0x04 {
623+
// RET_UNLINK: read status (4 bytes) + padding (24 bytes)
624+
let status = stream.read_i32().await?;
625+
let mut padding = [0u8; 24];
626+
stream.read_exact(&mut padding).await?;
627+
debug!(
628+
"Received RET_UNLINK for seqnum {}, status: {}",
629+
seqnum, status
630+
);
631+
return Ok(());
632+
}
633+
539634
let pending_request = self.pending.lock().await.remove(&seqnum);
540635

541636
if let Some((direction, tx)) = pending_request {

0 commit comments

Comments
 (0)