diff --git a/src/capture.rs b/src/capture.rs index c4536c8b..f45eb5a1 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -14,7 +14,7 @@ use crate::compact_index::{compact_index, CompactWriter, CompactReader}; use crate::rcu::SingleWriterRcu; use crate::vec_map::{Key, VecMap}; use crate::usb::{self, prelude::*, validate_packet}; -use crate::util::{fmt_count, fmt_size}; +use crate::util::{fmt_count, fmt_size, titlecase}; use anyhow::{Context, Error, bail}; use arc_swap::{ArcSwap, ArcSwapOption}; @@ -41,14 +41,14 @@ pub struct CaptureWriter { pub packet_index: CompactWriter, pub packet_times: CompactWriter, pub transaction_index: CompactWriter, - pub transfer_index: DataWriter, - pub item_index: CompactWriter, + pub group_index: DataWriter, + pub item_index: CompactWriter, pub devices: DataWriter, pub endpoints: DataWriter, pub endpoint_states: DataWriter, - pub endpoint_state_index: CompactWriter>, + pub endpoint_state_index: CompactWriter>, #[allow(dead_code)] - pub end_index: CompactWriter, + pub end_index: CompactWriter, } /// Cloneable handle for read access to a capture. @@ -60,14 +60,14 @@ pub struct CaptureReader { pub packet_index: CompactReader, pub packet_times: CompactReader, pub transaction_index: CompactReader, - pub transfer_index: DataReader, - pub item_index: CompactReader, + pub group_index: DataReader, + pub item_index: CompactReader, pub devices: DataReader, pub endpoints: DataReader, pub endpoint_states: DataReader, - pub endpoint_state_index: CompactReader>, + pub endpoint_state_index: CompactReader>, #[allow(dead_code)] - pub end_index: CompactReader, + pub end_index: CompactReader, } /// Create a capture reader-writer pair. @@ -80,7 +80,7 @@ pub fn create_capture() let (packets_writer, packets_reader) = compact_index()?; let (timestamp_writer, timestamp_reader) = compact_index()?; let (transactions_writer, transactions_reader) = compact_index()?; - let (transfers_writer, transfers_reader) = data_stream()?; + let (groups_writer, groups_reader) = data_stream()?; let (items_writer, items_reader) = compact_index()?; let (devices_writer, devices_reader) = data_stream()?; let (endpoints_writer, endpoints_reader) = data_stream()?; @@ -103,7 +103,7 @@ pub fn create_capture() packet_index: packets_writer, packet_times: timestamp_writer, transaction_index: transactions_writer, - transfer_index: transfers_writer, + group_index: groups_writer, item_index: items_writer, devices: devices_writer, endpoints: endpoints_writer, @@ -120,7 +120,7 @@ pub fn create_capture() packet_index: packets_reader, packet_times: timestamp_reader, transaction_index: transactions_reader, - transfer_index: transfers_reader, + group_index: groups_reader, item_index: items_reader, devices: devices_reader, endpoints: endpoints_reader, @@ -144,10 +144,10 @@ pub struct EndpointShared { pub struct EndpointWriter { pub shared: Arc, pub transaction_ids: CompactWriter, - pub transfer_index: CompactWriter, + pub group_index: CompactWriter, pub data_transactions: CompactWriter, pub data_byte_counts: CompactWriter, - pub end_index: CompactWriter, + pub end_index: CompactWriter, } /// Cloneable handle for read access to endpoint data. @@ -155,10 +155,10 @@ pub struct EndpointWriter { pub struct EndpointReader { pub shared: Arc, pub transaction_ids: CompactReader, - pub transfer_index: CompactReader, + pub group_index: CompactReader, pub data_transactions: CompactReader, pub data_byte_counts: CompactReader, - pub end_index: CompactReader, + pub end_index: CompactReader, } /// Create a per-endpoint reader-writer pair. @@ -167,7 +167,7 @@ pub fn create_endpoint() { // Create all the required streams. let (transactions_writer, transactions_reader) = compact_index()?; - let (transfers_writer, transfers_reader) = compact_index()?; + let (groups_writer, groups_reader) = compact_index()?; let (data_transaction_writer, data_transaction_reader) = compact_index()?; let (data_byte_count_writer, data_byte_count_reader) = compact_index()?; let (end_writer, end_reader) = compact_index()?; @@ -182,7 +182,7 @@ pub fn create_endpoint() let writer = EndpointWriter { shared: shared.clone(), transaction_ids: transactions_writer, - transfer_index: transfers_writer, + group_index: groups_writer, data_transactions: data_transaction_writer, data_byte_counts: data_byte_count_writer, end_index: end_writer, @@ -192,7 +192,7 @@ pub fn create_endpoint() let reader = EndpointReader { shared, transaction_ids: transactions_reader, - transfer_index: transfers_reader, + group_index: groups_reader, data_transactions: data_transaction_reader, data_byte_counts: data_byte_count_reader, end_index: end_reader, @@ -206,10 +206,10 @@ pub type PacketByteId = Id; pub type PacketId = Id; pub type Timestamp = u64; pub type TransactionId = Id; -pub type TransferId = Id; +pub type GroupId = Id; pub type EndpointTransactionId = Id; -pub type EndpointTransferId = Id; -pub type TrafficItemId = Id; +pub type EndpointGroupId = Id; +pub type TrafficItemId = Id; pub type DeviceId = Id; pub type EndpointId = Id; pub type EndpointDataEvent = u64; @@ -218,9 +218,9 @@ pub type DeviceVersion = u32; #[derive(Clone, Debug)] pub enum TrafficItem { - Transfer(TransferId), - Transaction(Option, TransactionId), - Packet(Option, Option, PacketId), + TransactionGroup(GroupId), + Transaction(Option, TransactionId), + Packet(Option, Option, PacketId), } #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] @@ -266,10 +266,10 @@ impl TrafficViewMode { #[derive(Clone, Debug)] pub struct DeviceItem { - device_id: DeviceId, - version: DeviceVersion, - content: DeviceItemContent, - indent: u8, + pub device_id: DeviceId, + pub version: DeviceVersion, + pub content: DeviceItemContent, + pub indent: u8, } #[derive(Clone, Debug)] @@ -329,13 +329,13 @@ impl std::fmt::Display for Endpoint { bitfield! { #[derive(Copy, Clone, Debug, Default, Pod, Zeroable)] #[repr(C)] - pub struct TransferIndexEntry(u64); - pub u64, from into EndpointTransferId, transfer_id, set_transfer_id: 51, 0; + pub struct GroupIndexEntry(u64); + pub u64, from into EndpointGroupId, group_id, set_group_id: 51, 0; pub u64, from into EndpointId, endpoint_id, set_endpoint_id: 62, 52; pub u8, _is_start, _set_is_start: 63, 63; } -impl TransferIndexEntry { +impl GroupIndexEntry { pub fn is_start(&self) -> bool { self._is_start() != 0 } @@ -370,9 +370,12 @@ pub enum EndpointType { impl std::fmt::Display for EndpointType { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use EndpointType::*; match self { - EndpointType::Normal(usb_type) => write!(f, "{usb_type:?}"), - special_type => write!(f, "{special_type:?}"), + Normal(usb_type) => std::fmt::Display::fmt(&usb_type, f), + Unidentified => write!(f, "unidentified"), + Framing => write!(f, "framing"), + Invalid => write!(f, "invalid"), } } } @@ -681,7 +684,7 @@ pub struct Transaction { split: Option<(SplitFields, PID)>, pub packet_id_range: Range, data_packet_id: Option, - payload_byte_range: Option>>, + pub payload_byte_range: Option>>, } #[derive(PartialEq)] @@ -834,6 +837,26 @@ impl Transaction { } } +pub struct Group { + pub endpoint_id: EndpointId, + pub endpoint: Endpoint, + pub endpoint_type: EndpointType, + pub range: Range, + pub count: u64, + pub content: GroupContent, + pub is_start: bool, +} + +pub enum GroupContent { + Request(ControlTransfer), + Data(Range), + Polling(u64), + Ambiguous(Range, u64), + IncompleteRequest, + Framing, + Invalid, +} + struct Bytes<'src> { partial: bool, bytes: &'src [u8], @@ -943,7 +966,7 @@ impl CaptureWriter { let mut overhead: u64 = self.packet_index.size() + self.transaction_index.size() + - self.transfer_index.size() + + self.group_index.size() + self.endpoint_states.size() + self.endpoint_state_index.size(); let mut trx_count = 0; @@ -953,8 +976,8 @@ impl CaptureWriter { for ep_traf in self.shared.endpoint_readers.load().as_ref() { trx_count += ep_traf.transaction_ids.len(); trx_size += ep_traf.transaction_ids.size(); - xfr_count += ep_traf.transfer_index.len(); - xfr_size += ep_traf.transfer_index.size(); + xfr_count += ep_traf.group_index.len(); + xfr_size += ep_traf.group_index.size(); overhead += trx_size + xfr_size; } let ratio = (overhead as f32) / (self.packet_data.size() as f32); @@ -964,16 +987,16 @@ impl CaptureWriter { " Packet data: {}\n", " Packet index: {}\n", " Transaction index: {}\n", - " Transfer index: {}\n", + " Transaction group index: {}\n", " Endpoint states: {}\n", " Endpoint state index: {}\n", " Endpoint transaction indices: {} values, {}\n", - " Endpoint transfer indices: {} values, {}\n", + " Endpoint transaction group indices: {} values, {}\n", "Total overhead: {:.1}% ({})\n"), fmt_size(self.packet_data.size()), &self.packet_index, &self.transaction_index, - &self.transfer_index, + &self.group_index, &self.endpoint_states, &self.endpoint_state_index, fmt_count(trx_count), fmt_size(trx_size), @@ -1004,14 +1027,14 @@ impl CaptureReader { Ok(self.endpoint_readers.get_mut(endpoint_id).unwrap()) } - fn transfer_range(&mut self, entry: &TransferIndexEntry) + fn group_range(&mut self, entry: &GroupIndexEntry) -> Result, Error> { let endpoint_id = entry.endpoint_id(); - let ep_transfer_id = entry.transfer_id(); + let ep_group_id = entry.group_id(); let ep_traf = self.endpoint_traffic(endpoint_id)?; - ep_traf.transfer_index.target_range( - ep_transfer_id, ep_traf.transaction_ids.len()) + ep_traf.group_index.target_range( + ep_group_id, ep_traf.transaction_ids.len()) } fn transaction_fields(&mut self, transaction: &Transaction) @@ -1039,7 +1062,7 @@ impl CaptureReader { } } - fn transaction_bytes(&mut self, transaction: &Transaction) + pub fn transaction_bytes(&mut self, transaction: &Transaction) -> Result, Error> { let data_packet_id = transaction.data_packet_id @@ -1077,11 +1100,11 @@ impl CaptureReader { Ok(transfer_bytes) } - fn endpoint_state(&mut self, transfer_id: TransferId) + fn endpoint_state(&mut self, group_id: GroupId) -> Result, Error> { let range = self.endpoint_state_index.target_range( - transfer_id, self.endpoint_states.len())?; + group_id, self.endpoint_states.len())?; self.endpoint_states.get_range(&range) } @@ -1186,15 +1209,75 @@ impl CaptureReader { }) } + pub fn group(&mut self, group_id: GroupId) -> Result { + let entry = self.group_index.get(group_id)?; + let endpoint_id = entry.endpoint_id(); + let endpoint = self.endpoints.get(endpoint_id)?; + let device_id = endpoint.device_id(); + let dev_data = self.device_data(device_id)?; + let ep_addr = endpoint.address(); + let (endpoint_type, _) = dev_data.endpoint_details(ep_addr); + let range = self.group_range(&entry)?; + let count = range.len(); + let content = match endpoint_type { + EndpointType::Invalid => GroupContent::Invalid, + EndpointType::Framing => GroupContent::Framing, + EndpointType::Normal(usb::EndpointType::Control) => { + let addr = endpoint.device_address(); + match self.control_transfer(addr, endpoint_id, &range) { + Ok(transfer) => GroupContent::Request(transfer), + Err(_) => GroupContent::IncompleteRequest, + } + }, + _ => { + let ep_group_id = entry.group_id(); + let ep_traf = self.endpoint_traffic(endpoint_id)?; + let range = ep_traf.group_index.target_range( + ep_group_id, ep_traf.transaction_ids.len())?; + let first_transaction_id = + ep_traf.transaction_ids.get(range.start)?; + let first_transaction = + self.transaction(first_transaction_id)?; + let count = if first_transaction.split.is_some() { + (count + 1) / 2 + } else { + count + }; + match first_transaction.result(endpoint_type) { + TransactionResult::Success => { + let ep_traf = self.endpoint_traffic(endpoint_id)?; + let data_range = ep_traf.transfer_data_range(&range)?; + GroupContent::Data(data_range) + }, + TransactionResult::Ambiguous => { + let ep_traf = self.endpoint_traffic(endpoint_id)?; + let data_range = ep_traf.transfer_data_range(&range)?; + GroupContent::Ambiguous(data_range, count) + }, + TransactionResult::Failure => GroupContent::Polling(count), + } + } + }; + Ok(Group { + endpoint_id, + endpoint, + endpoint_type, + range, + count, + content, + is_start: entry.is_start(), + }) + } + fn control_transfer(&mut self, address: DeviceAddr, endpoint_id: EndpointId, - range: Range) + range: &Range) -> Result { let ep_traf = self.endpoint_traffic(endpoint_id)?; - let transaction_ids = ep_traf.transaction_ids.get_range(&range)?; - let data_range = ep_traf.transfer_data_range(&range)?; + let transaction_ids = ep_traf.transaction_ids.get_range(range)?; + let data_range = ep_traf.transfer_data_range(range)?; let data_length = ep_traf .transfer_data_length(&data_range)? .try_into()?; @@ -1223,17 +1306,17 @@ impl CaptureReader { .clone()) } - fn transfer_extended(&mut self, - endpoint_id: EndpointId, - transfer_id: TransferId) - -> Result - { + fn group_extended( + &mut self, + endpoint_id: EndpointId, + group_id: GroupId + ) -> Result { use EndpointState::*; - let count = self.transfer_index.len(); - if transfer_id.value + 1 >= count { + let count = self.group_index.len(); + if group_id.value + 1 >= count { return Ok(false); }; - let state = self.endpoint_state(transfer_id + 1)?; + let state = self.endpoint_state(group_id + 1)?; Ok(match state.get(endpoint_id.value as usize) { Some(ep_state) => EndpointState::from(*ep_state) == Ongoing, None => false @@ -1329,8 +1412,8 @@ impl ItemSource for CaptureReader { None => Ok(match view_mode { Hierarchical => { let item_id = TrafficItemId::from(index); - let transfer_id = self.item_index.get(item_id)?; - Transfer(transfer_id) + let group_id = self.item_index.get(item_id)?; + TransactionGroup(group_id) }, Transactions => Transaction(None, TransactionId::from(index)), @@ -1352,17 +1435,17 @@ impl ItemSource for CaptureReader { { use TrafficItem::*; Ok(match parent { - Transfer(transfer_id) => - Transaction(Some(*transfer_id), { - let entry = self.transfer_index.get(*transfer_id)?; + TransactionGroup(group_id) => + Transaction(Some(*group_id), { + let entry = self.group_index.get(*group_id)?; let endpoint_id = entry.endpoint_id(); - let ep_transfer_id = entry.transfer_id(); + let ep_group_id = entry.group_id(); let ep_traf = self.endpoint_traffic(endpoint_id)?; - let offset = ep_traf.transfer_index.get(ep_transfer_id)?; + let offset = ep_traf.group_index.get(ep_group_id)?; ep_traf.transaction_ids.get(offset + index)? }), - Transaction(transfer_id_opt, transaction_id) => - Packet(*transfer_id_opt, Some(*transaction_id), { + Transaction(group_id_opt, transaction_id) => + Packet(*group_id_opt, Some(*transaction_id), { self.transaction_index.get(*transaction_id)? + index}), Packet(..) => bail!("Packets have no child items") }) @@ -1384,14 +1467,14 @@ impl ItemSource for CaptureReader { Packets => self.packet_index.len(), }) }, - Some(Transfer(transfer_id)) => { - let entry = self.transfer_index.get(*transfer_id)?; + Some(TransactionGroup(group_id)) => { + let entry = self.group_index.get(*group_id)?; if !entry.is_start() { return Ok((Complete, 0)); } - let transaction_count = self.transfer_range(&entry)?.len(); + let transaction_count = self.group_range(&entry)?.len(); let ep_traf = self.endpoint_traffic(entry.endpoint_id())?; - if entry.transfer_id().value >= ep_traf.end_index.len() { + if entry.group_id().value >= ep_traf.end_index.len() { (Ongoing, transaction_count) } else { (Complete, transaction_count) @@ -1537,7 +1620,7 @@ impl ItemSource for CaptureReader { } s }, - Transaction(transfer_id_opt, transaction_id) => { + Transaction(group_id_opt, transaction_id) => { let num_packets = self.packet_index.len(); let packet_id_range = self.transaction_index.target_range( *transaction_id, num_packets)?; @@ -1572,9 +1655,9 @@ impl ItemSource for CaptureReader { split.hub_address(), split.port())) } - let endpoint_id = match transfer_id_opt { - Some(transfer_id) => { - let entry = self.transfer_index.get(*transfer_id)?; + let endpoint_id = match group_id_opt { + Some(group_id) => { + let entry = self.group_index.get(*group_id)?; entry.endpoint_id() }, None => match self.shared.packet_endpoint( @@ -1596,32 +1679,22 @@ impl ItemSource for CaptureReader { } s }, - Transfer(transfer_id) => { - use EndpointType::*; - use usb::EndpointType::*; - use TransactionResult::*; - let entry = self.transfer_index.get(*transfer_id)?; - let endpoint_id = entry.endpoint_id(); - let endpoint = self.endpoints.get(endpoint_id)?; - let device_id = endpoint.device_id(); - let dev_data = self.device_data(device_id)?; - let ep_addr = endpoint.address(); - let (ep_type, _) = dev_data.endpoint_details(ep_addr); - let range = self.transfer_range(&entry)?; - let count = range.len(); - if detail && entry.is_start() { - let ep_traf = self.endpoint_traffic(entry.endpoint_id())?; - let start_ep_transaction_id = - ep_traf.transfer_index.get(entry.transfer_id())?; + TransactionGroup(group_id) => { + use GroupContent::*; + let group = self.group(*group_id)?; + if detail && group.is_start { + let ep_traf = + self.endpoint_traffic(group.endpoint_id)?; + let start_ep_transaction_id = group.range.start; let start_transaction_id = ep_traf.transaction_ids.get(start_ep_transaction_id)?; let start_packet_id = self.transaction_index.get(start_transaction_id)?; - if count == 1 { + if group.count == 1 { writeln!(s, "Transaction group with 1 transaction")?; } else { writeln!(s, "Transaction group with {} transactions", - count)?; + group.count)?; } writeln!(s, "Timestamp: {} ns from start of capture", fmt_count(self.packet_time(start_packet_id)?))?; @@ -1629,7 +1702,11 @@ impl ItemSource for CaptureReader { start_transaction_id.value + 1, start_packet_id.value + 1)?; } - match (ep_type, entry.is_start()) { + let endpoint = &group.endpoint; + let endpoint_type = group.endpoint_type; + let addr = group.endpoint.device_address(); + let count = group.count; + match (group.content, group.is_start) { (Invalid, true) => write!(s, "{count} invalid groups"), (Invalid, false) => write!(s, @@ -1638,82 +1715,56 @@ impl ItemSource for CaptureReader { "{count} SOF groups"), (Framing, false) => write!(s, "End of SOF groups"), - (Normal(Control), true) => { - let addr = endpoint.device_address(); - match self.control_transfer(addr, endpoint_id, range) { - Ok(transfer) if detail => write!(s, - "Control transfer on device {addr}\n{}", - transfer.summary()), - Ok(transfer) => write!(s, - "{}", transfer.summary()), - Err(_) => write!(s, - "Incomplete control transfer on device {addr}") + (Request(transfer), true) if detail => write!(s, + "Control transfer on device {addr}\n{}", + transfer.summary()), + (Request(transfer), true) => write!(s, + "{}", transfer.summary()), + (IncompleteRequest, true) => write!(s, + "Incomplete control transfer on device {addr}"), + (Request(_) | IncompleteRequest, false) => write!(s, + "End of control transfer on device {addr}"), + (Data(data_range), true) => { + let ep_traf = + self.endpoint_traffic(group.endpoint_id)?; + let length = + ep_traf.transfer_data_length(&data_range)?; + let length_string = fmt_size(length); + let max = if detail { 1024 } else { 100 }; + let display_length = min(length, max) as usize; + let transfer_bytes = self.transfer_bytes( + group.endpoint_id, &data_range, display_length)?; + let display_bytes = Bytes { + partial: length > display_length as u64, + bytes: &transfer_bytes, + }; + let ep_type_string = titlecase( + &format!("{endpoint_type}")); + write!(s, "{ep_type_string} transfer ")?; + write!(s, "of {length_string} ")?; + write!(s, "on endpoint {endpoint}")?; + if detail { + write!(s, "\nPayload: {display_bytes}") + } else { + write!(s, ": {display_bytes}") } }, - (Normal(Control), false) => { - let addr = endpoint.device_address(); - write!(s, "End of control transfer on device {addr}") - }, - (endpoint_type, starting) => { - let ep_transfer_id = entry.transfer_id(); - let ep_traf = self.endpoint_traffic(endpoint_id)?; - let range = ep_traf.transfer_index.target_range( - ep_transfer_id, ep_traf.transaction_ids.len())?; - let first_transaction_id = - ep_traf.transaction_ids.get(range.start)?; - let first_transaction = - self.transaction(first_transaction_id)?; - let ep_type_string = format!("{endpoint_type}"); - let ep_type_lower = ep_type_string.to_lowercase(); - let count = if first_transaction.split.is_some() { - (count + 1) / 2 - } else { - count - }; - match (first_transaction.result(ep_type), starting) { - (Success, true) => { - let ep_traf = - self.endpoint_traffic(endpoint_id)?; - let data_range = - ep_traf.transfer_data_range(&range)?; - let length = - ep_traf.transfer_data_length(&data_range)?; - let length_string = fmt_size(length); - let max = if detail { 1024 } else { 100 }; - let display_length = min(length, max) as usize; - let transfer_bytes = self.transfer_bytes( - endpoint_id, &data_range, display_length)?; - let display_bytes = Bytes { - partial: length > display_length as u64, - bytes: &transfer_bytes, - }; - write!(s, "{ep_type_string} transfer ")?; - write!(s, "of {length_string} ")?; - write!(s, "on endpoint {endpoint}")?; - if detail { - write!(s, "\nPayload: {display_bytes}") - } else { - write!(s, ": {display_bytes}") - } - }, - (Success, false) => write!(s, - "End of {ep_type_lower} transfer on endpoint {endpoint}"), - (Failure, true) => write!(s, - "Polling {count} times for {ep_type_lower} transfer on endpoint {endpoint}"), - (Failure, false) => write!(s, - "End polling for {ep_type_lower} transfer on endpoint {endpoint}"), - (Ambiguous, true) => { - write!(s, "{count} ambiguous transactions on endpoint {endpoint}")?; - if detail { - write!(s, "\nThe result of these transactions is ambiguous because the endpoint type is not known.")?; - write!(s, "\nTry starting the capture before this device is enumerated, so that its descriptors are captured.")?; - } - Ok(()) - }, - (Ambiguous, false) => write!(s, - "End of ambiguous transactions."), + (Data(_), false) => write!(s, + "End of {endpoint_type} transfer on endpoint {endpoint}"), + (Polling(count), true) => write!(s, + "Polling {count} times for {endpoint_type} transfer on endpoint {endpoint}"), + (Polling(_count), false) => write!(s, + "End polling for {endpoint_type} transfer on endpoint {endpoint}"), + (Ambiguous(_data_range, count), true) => { + write!(s, "{count} ambiguous transactions on endpoint {endpoint}")?; + if detail { + write!(s, "\nThe result of these transactions is ambiguous because the endpoint type is not known.")?; + write!(s, "\nTry starting the capture before this device is enumerated, so that its descriptors are captured.")?; } - } + Ok(()) + }, + (Ambiguous(..), false) => write!(s, + "End of ambiguous transactions."), }?; s } @@ -1738,29 +1789,31 @@ impl ItemSource for CaptureReader { }; if view_mode == Transactions { return Ok(String::from(match (item, last_packet) { - (Transfer(_), _) => unreachable!(), - (Transaction(..), _) => "○", - (Packet(..), false) => "├──", - (Packet(..), true ) => "└──", + (TransactionGroup(_), _) => unreachable!(), + (Transaction(..), _) => "○", + (Packet(..), false) => "├──", + (Packet(..), true ) => "└──", })); } let endpoint_count = self.endpoints.len() as usize; let max_string_length = endpoint_count + " └──".len(); let mut connectors = String::with_capacity(max_string_length); - let transfer_id = match item { - Transfer(i) | Transaction(Some(i), _) | Packet(Some(i), ..) => *i, + let group_id = match item { + TransactionGroup(i) | + Transaction(Some(i), _) | + Packet(Some(i), ..) => *i, _ => unreachable!() }; - let entry = self.transfer_index.get(transfer_id)?; + let entry = self.group_index.get(group_id)?; let endpoint_id = entry.endpoint_id(); - let endpoint_state = self.endpoint_state(transfer_id)?; - let extended = self.transfer_extended(endpoint_id, transfer_id)?; + let endpoint_state = self.endpoint_state(group_id)?; + let extended = self.group_extended(endpoint_id, group_id)?; let ep_traf = self.endpoint_traffic(endpoint_id)?; let last_transaction = match item { Transaction(_, transaction_id) | Packet(_, Some(transaction_id), _) => { - let range = ep_traf.transfer_index.target_range( - entry.transfer_id(), ep_traf.transaction_ids.len())?; + let range = ep_traf.group_index.target_range( + entry.group_id(), ep_traf.transaction_ids.len())?; let last_transaction_id = ep_traf.transaction_ids.get(range.end - 1)?; *transaction_id == last_transaction_id @@ -1773,12 +1826,12 @@ impl ItemSource for CaptureReader { let active = state != Idle; let on_endpoint = i == endpoint_id.value as usize; thru |= match (item, state, on_endpoint) { - (Transfer(..), Starting | Ending, _) => true, + (TransactionGroup(..), Starting | Ending, _) => true, (Transaction(..) | Packet(..), _, true) => on_endpoint, _ => false, }; connectors.push(match item { - Transfer(..) => { + TransactionGroup(..) => { match (state, thru) { (Idle, false) => ' ', (Idle, true ) => '─', @@ -1811,18 +1864,18 @@ impl ItemSource for CaptureReader { let state_length = endpoint_state.len(); for _ in state_length..endpoint_count { connectors.push(match item { - Transfer(..) => '─', - Transaction(..) => '─', - Packet(..) => ' ', + TransactionGroup(..) => '─', + Transaction(..) => '─', + Packet(..) => ' ', }); } connectors.push_str( match (item, last_packet) { - (Transfer(_), _) if entry.is_start() => "─", - (Transfer(_), _) => "──□ ", - (Transaction(..), _) => "───", - (Packet(..), false) => " ├──", - (Packet(..), true) => " └──", + (TransactionGroup(_), _) if entry.is_start() => "─", + (TransactionGroup(_), _) => "──□ ", + (Transaction(..), _) => "───", + (Packet(..), false) => " ├──", + (Packet(..), true) => " └──", } ); Ok(connectors) @@ -1833,11 +1886,11 @@ impl ItemSource for CaptureReader { { use TrafficItem::*; let packet_id = match item { - Transfer(transfer_id) => { - let entry = self.transfer_index.get(*transfer_id)?; + TransactionGroup(group_id) => { + let entry = self.group_index.get(*group_id)?; let ep_traf = self.endpoint_traffic(entry.endpoint_id())?; let ep_transaction_id = - ep_traf.transfer_index.get(entry.transfer_id())?; + ep_traf.group_index.get(entry.group_id())?; let transaction_id = ep_traf.transaction_ids.get(ep_transaction_id)?; self.transaction_index.get(transaction_id)? @@ -2296,12 +2349,14 @@ pub mod prelude { EndpointReader, EndpointWriter, EndpointTransactionId, - EndpointTransferId, + EndpointGroupId, PacketId, TrafficItemId, TransactionId, - TransferId, - TransferIndexEntry, + GroupId, + Group, + GroupContent, + GroupIndexEntry, INVALID_EP_NUM, FRAMING_EP_NUM, INVALID_EP_ID, diff --git a/src/decoder.rs b/src/decoder.rs index 444e2885..6ea658e6 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -12,9 +12,9 @@ struct EndpointData { device_id: DeviceId, address: EndpointAddr, writer: EndpointWriter, - early_start: Option, - active: Option, - ended: Option, + early_start: Option, + active: Option, + ended: Option, last_success: bool, setup: Option, payload: Vec, @@ -22,14 +22,14 @@ struct EndpointData { total_data: u64, } -struct TransferState { - id: EndpointTransferId, +struct GroupState { + id: EndpointGroupId, first: PID, last: Option, } #[derive(PartialEq, Eq)] -enum TransferStatus { +enum GroupStatus { Single, New, Continue, @@ -271,13 +271,13 @@ enum TransactionSideEffect { } impl EndpointData { - fn transfer_status(&mut self, - dev_data: &DeviceData, - transaction: &mut TransactionState, - success: bool, - complete: bool) - -> Result<(TransferStatus, TransactionSideEffect), Error> - { + fn group_status( + &mut self, + dev_data: &DeviceData, + transaction: &mut TransactionState, + success: bool, + complete: bool + ) -> Result<(GroupStatus, TransactionSideEffect), Error> { use TransactionStyle::*; let (ep_type, ep_max) = dev_data.endpoint_details(self.address); let split_sc = match transaction.style { @@ -302,7 +302,7 @@ impl EndpointData { use EndpointType::{Normal, Framing}; use usb::EndpointType::*; use Direction::*; - use TransferStatus::*; + use GroupStatus::*; use StartComplete::*; use TransactionSideEffect::*; let mut effect = NoEffect; @@ -321,7 +321,7 @@ impl EndpointData { }, (Normal(Control), - Some(TransferState { + Some(GroupState { last: Some(last), ..}), _) => match &self.setup { // No control transaction is valid unless setup was done. @@ -391,8 +391,8 @@ impl EndpointData { }, // An IN or OUT transaction on a non-control endpoint, - // with no transfer in progress, starts a new transfer. - // This can be either an actual transfer, or a polling + // with no group in progress, starts a new group. + // This can be either a data transfer, or a polling // group used to collect NAKed transactions. (_, None, IN | OUT) => { if success { @@ -420,8 +420,8 @@ impl EndpointData { }, // IN or OUT may then be repeated. - (_, Some(TransferState { first: IN, ..}), IN) | - (_, Some(TransferState { first: OUT, ..}), OUT) => { + (_, Some(GroupState { first: IN, ..}), IN) | + (_, Some(GroupState { first: OUT, ..}), OUT) => { if success { if let Some(data) = payload { if split_sc == Some(Start) && next == OUT { @@ -462,16 +462,16 @@ impl EndpointData { }, // OUT may also be followed by PING. - (_, Some(TransferState { first: OUT, .. }), PING) => Retry, + (_, Some(GroupState { first: OUT, .. }), PING) => Retry, - // A SOF group starts a special transfer, unless + // A SOF transaction starts a singleton framing group, unless // one is already in progress. (Framing, None, SOF) => New, - // Further SOF groups continue this transfer. + // Further SOF transactions continue this singleton group. (Framing, _, SOF) => Continue, - // Any other case is not a valid part of a transfer. + // Any other case is not a valid part of a group. _ => Invalid }; Ok((status, effect)) @@ -618,11 +618,11 @@ impl Decoder { New => { self.transaction_end(false, false)?; self.transaction_start(packet_id, pid, packet)?; - self.transfer_early_append()?; + self.group_early_append()?; }, Continue => { self.transaction_append(pid, packet)?; - self.transfer_early_append()?; + self.group_early_append()?; }, Done | Retry | Fail => { self.transaction_append(pid, packet)?; @@ -677,8 +677,8 @@ impl Decoder { setup: None, payload: None, }; - // Some packets start a new transfer immediately. - self.transfer_early_start(&mut state, pid)?; + // Some packets start a new group immediately. + self.group_early_start(&mut state, pid)?; self.transaction_state = Some(state); Ok(()) } @@ -716,7 +716,7 @@ impl Decoder { { if let Some(mut state) = self.transaction_state.take() { if state.endpoint_id.is_some() { - self.transfer_update(&mut state, success, complete)?; + self.group_update(&mut state, success, complete)?; } } Ok(()) @@ -762,16 +762,16 @@ impl Decoder { Ok(endpoint_id) } - fn transfer_early_start(&mut self, - transaction: &mut TransactionState, - start: PID) - -> Result<(), Error> - { + fn group_early_start( + &mut self, + transaction: &mut TransactionState, + start: PID + ) -> Result<(), Error> { use PID::*; let start_early = match (start, transaction.endpoint_id) { - // SETUP always starts a new transfer. + // SETUP always starts a new control transfer. (SETUP, Some(endpoint_id)) => Some(endpoint_id), - // Other PIDs always start a new transfer if there + // Other PIDs always start a new group if there // is no existing one on their endpoint. (IN | OUT | SOF | Malformed, Some(endpoint_id)) => { let ep_data = &self.endpoint_data[endpoint_id]; @@ -786,20 +786,20 @@ impl Decoder { }; if let Some(endpoint_id) = start_early { - let ep_transfer_id = - self.add_transfer(endpoint_id, transaction)?; + let ep_group_id = + self.add_group(endpoint_id, transaction)?; let ep_data = &mut self.endpoint_data[endpoint_id]; - ep_data.early_start = Some(ep_transfer_id); + ep_data.early_start = Some(ep_group_id); } Ok(()) } - fn transfer_early_append(&mut self) -> Result<(), Error> { + fn group_early_append(&mut self) -> Result<(), Error> { use PID::*; use TransactionStyle::*; // Decide whether to index this transaction now. - // If this transaction might change the transfer sequence + // If this transaction might change the group sequence // and we can't tell yet, we can't index it yet. let to_index = if let Some(TransactionState { @@ -812,10 +812,10 @@ impl Decoder { { let ep_data = &self.endpoint_data[*endpoint_id]; match ep_data.active { - // IN and OUT transfers may start and end depending on + // IN and OUT groups may start and end depending on // transaction success and whether a packet is short. - Some(TransferState { first: IN | OUT, .. }) => None, - // In all other transfer states, it should be safe to index + Some(GroupState { first: IN | OUT, .. }) => None, + // In all other group states, it should be safe to index // the current transaction immediately. _ => Some((*endpoint_id, *transaction_id)) } @@ -835,63 +835,63 @@ impl Decoder { Ok(()) } - fn transfer_update(&mut self, - transaction: &mut TransactionState, - success: bool, - complete: bool) - -> Result<(), Error> - { - use TransferStatus::*; + fn group_update( + &mut self, + transaction: &mut TransactionState, + success: bool, + complete: bool + ) -> Result<(), Error> { + use GroupStatus::*; let endpoint_id = transaction.endpoint_id()?; let ep_data = &mut self.endpoint_data[endpoint_id]; let dev_data = self.capture.device_data(ep_data.device_id)?; - let (status, effect) = ep_data.transfer_status( + let (status, effect) = ep_data.group_status( dev_data.as_ref(), transaction, success, complete)?; match status { Single => { - self.transfer_start(transaction, true)?; - self.transfer_end(transaction)?; + self.group_start(transaction, true)?; + self.group_end(transaction)?; }, New => { - self.transfer_start(transaction, true)?; + self.group_start(transaction, true)?; }, Continue => { - self.transfer_append(transaction, true)?; + self.group_append(transaction, true)?; }, Retry => { - self.transfer_append(transaction, false)?; + self.group_append(transaction, false)?; }, Done => { - self.transfer_append(transaction, true)?; - self.transfer_end(transaction)?; + self.group_append(transaction, true)?; + self.group_end(transaction)?; }, Invalid => { - self.transfer_start(transaction, false)?; - self.transfer_end(transaction)?; + self.group_start(transaction, false)?; + self.group_end(transaction)?; } } self.endpoint_data[endpoint_id].apply_effect(transaction, effect)?; Ok(()) } - fn transfer_start(&mut self, - transaction: &mut TransactionState, - done: bool) - -> Result<(), Error> - { + fn group_start( + &mut self, + transaction: &mut TransactionState, + done: bool + ) -> Result<(), Error> { let endpoint_id = transaction.endpoint_id()?; let ep_data = &mut self.endpoint_data[endpoint_id]; - let ep_transfer_id = - if let Some(ep_transfer_id) = ep_data.early_start.take() { - ep_transfer_id + let ep_group_id = + if let Some(ep_group_id) = ep_data.early_start.take() { + ep_group_id } else { - self.add_transfer(endpoint_id, transaction)? + self.add_group(endpoint_id, transaction)? }; let transaction_type = transaction.start_pid()?; let ep_data = &mut self.endpoint_data[endpoint_id]; ep_data.active = Some( - TransferState { - id: ep_transfer_id, + GroupState { + id: ep_group_id, first: transaction_type, last: if done { Some(transaction_type) } else { None }, } @@ -900,55 +900,55 @@ impl Decoder { Ok(()) } - fn transfer_append(&mut self, - transaction: &mut TransactionState, - done: bool) - -> Result<(), Error> - { + fn group_append( + &mut self, + transaction: &mut TransactionState, + done: bool + ) -> Result<(), Error> { let endpoint_id = transaction.endpoint_id()?; let ep_data = &mut self.endpoint_data[endpoint_id]; - if let Some(transfer) = &mut ep_data.active { + if let Some(group) = &mut ep_data.active { if transaction.ep_transaction_id.is_none() { let ep_transaction_id = ep_data.writer.transaction_ids.push(transaction.id)?; transaction.ep_transaction_id = Some(ep_transaction_id); } if done { - transfer.last = Some(transaction.start_pid()?); + group.last = Some(transaction.start_pid()?); } } else { - self.transfer_start(transaction, done)?; + self.group_start(transaction, done)?; } Ok(()) } - fn transfer_end(&mut self, transaction: &TransactionState) + fn group_end(&mut self, transaction: &TransactionState) -> Result<(), Error> { let endpoint_id = transaction.endpoint_id()?; let ep_data = &mut self.endpoint_data[endpoint_id]; ep_data.payload.clear(); - if let Some(transfer) = ep_data.active.take() { - let ep_transfer_id = transfer.id; - ep_data.ended = Some(ep_transfer_id); - let transfer_end_id = - self.add_transfer_entry(endpoint_id, ep_transfer_id, false)?; + if let Some(group) = ep_data.active.take() { + let ep_group_id = group.id; + ep_data.ended = Some(ep_group_id); + let group_end_id = + self.add_group_entry(endpoint_id, ep_group_id, false)?; if self.last_item_endpoint != Some(endpoint_id) { - self.add_item(endpoint_id, transfer_end_id)?; + self.add_item(endpoint_id, group_end_id)?; } } Ok(()) } - fn add_transfer(&mut self, - endpoint_id: EndpointId, - transaction: &mut TransactionState) - -> Result - { + fn add_group( + &mut self, + endpoint_id: EndpointId, + transaction: &mut TransactionState + ) -> Result { let ep_data = &mut self.endpoint_data[endpoint_id]; - if let Some(transfer) = ep_data.active.take() { - ep_data.ended = Some(transfer.id); - self.add_transfer_entry(endpoint_id, transfer.id, false)?; + if let Some(group) = ep_data.active.take() { + ep_data.ended = Some(group.id); + self.add_group_entry(endpoint_id, group.id, false)?; } let ep_transaction_id = if let Some(ep_transaction_id) = transaction.ep_transaction_id { @@ -961,33 +961,33 @@ impl Decoder { ep_transaction_id }; let ep_data = &mut self.endpoint_data[endpoint_id]; - let ep_transfer_id = - ep_data.writer.transfer_index.push(ep_transaction_id)?; - let transfer_start_id = - self.add_transfer_entry(endpoint_id, ep_transfer_id, true)?; - self.add_item(endpoint_id, transfer_start_id)?; - Ok(ep_transfer_id) + let ep_group_id = + ep_data.writer.group_index.push(ep_transaction_id)?; + let group_start_id = + self.add_group_entry(endpoint_id, ep_group_id, true)?; + self.add_item(endpoint_id, group_start_id)?; + Ok(ep_group_id) } - fn add_transfer_entry(&mut self, - endpoint_id: EndpointId, - ep_transfer_id: EndpointTransferId, - start: bool) - -> Result - { + fn add_group_entry( + &mut self, + endpoint_id: EndpointId, + ep_group_id: EndpointGroupId, + start: bool + ) -> Result { self.add_endpoint_state(endpoint_id, start)?; - let mut entry = TransferIndexEntry::default(); + let mut entry = GroupIndexEntry::default(); entry.set_endpoint_id(endpoint_id); - entry.set_transfer_id(ep_transfer_id); + entry.set_group_id(ep_group_id); entry.set_is_start(start); - let transfer_id = self.capture.transfer_index.push(&entry)?; - Ok(transfer_id) + let group_id = self.capture.group_index.push(&entry)?; + Ok(group_id) } fn add_endpoint_state(&mut self, endpoint_id: EndpointId, start: bool) - -> Result + -> Result { let endpoint_count = self.capture.endpoints.len() as usize; for i in 0..endpoint_count { @@ -1011,21 +1011,21 @@ impl Decoder { fn add_item(&mut self, item_endpoint_id: EndpointId, - transfer_id: TransferId) + group_id: GroupId) -> Result { - let item_id = self.capture.item_index.push(transfer_id)?; + let item_id = self.capture.item_index.push(group_id)?; self.last_item_endpoint = Some(item_endpoint_id); - // Look for ended transfers which still need to be linked to an item. + // Look for ended groups which still need to be linked to an item. let endpoint_count = self.capture.endpoints.len(); for i in 0..endpoint_count { let endpoint_id = EndpointId::from(i); let ep_data = &mut self.endpoint_data[endpoint_id]; - if let Some(ep_transfer_id) = ep_data.ended.take() { - // This transfer has ended and is not yet linked to an item. + if let Some(ep_group_id) = ep_data.ended.take() { + // This group has ended and is not yet linked to an item. let end_id = ep_data.writer.end_index.push(item_id)?; - assert!(end_id == ep_transfer_id); + assert!(end_id == ep_group_id); } } diff --git a/src/item_widget.rs b/src/item_widget.rs index 6911227b..5c74881f 100644 --- a/src/item_widget.rs +++ b/src/item_widget.rs @@ -5,11 +5,15 @@ use gtk::{ self, prelude::*, subclass::prelude::*, - glib::{self, SignalHandlerId}, + gdk::Rectangle, + glib::{self, SignalHandlerId, clone}, pango::EllipsizeMode, + EventSequenceState, Expander, + GestureClick, Label, Orientation, + PopoverMenu, }; glib::wrapper! { @@ -23,6 +27,34 @@ impl ItemWidget { /// Create a new widget. pub fn new() -> ItemWidget { let wrapper: ItemWidget = glib::Object::new::(); + let right_click = GestureClick::new(); + right_click.set_button(3); + right_click.connect_released(clone!(@strong wrapper => + move |gesture, _n, x, y| { + if let Some(context_menu_fn) = wrapper + .imp() + .context_menu_fn + .borrow_mut() + .as_mut() + { + if let Some(new_popover) = context_menu_fn() { + gesture.set_state(EventSequenceState::Claimed); + let mut current_popover = + wrapper.imp().popover.borrow_mut(); + if let Some(old_popover) = current_popover.take() { + old_popover.unparent(); + } + new_popover.set_parent(&wrapper.clone()); + new_popover.set_pointing_to( + Some(&Rectangle::new(x as i32, y as i32, 1, 1)) + ); + new_popover.popup(); + current_popover.replace(new_popover); + } + } + } + )); + wrapper.add_controller(right_click); wrapper.imp().text_label.replace( Label::builder() .ellipsize(EllipsizeMode::End) @@ -62,6 +94,13 @@ impl ItemWidget { self.imp().conn_label.borrow_mut().set_markup( format!("{connectors}").as_str()); } + + /// Set the function to build the context menu for this widget. + pub fn set_context_menu_fn(&self, context_menu_fn: F) + where F: FnMut() -> Option + 'static + { + self.imp().context_menu_fn.replace(Some(Box::new(context_menu_fn))); + } } impl Default for ItemWidget { @@ -74,13 +113,17 @@ impl Default for ItemWidget { mod imp { use gtk::{ self, + prelude::*, subclass::prelude::*, glib::{self, SignalHandlerId}, Expander, Label, + PopoverMenu, }; use std::cell::RefCell; + type PopoverFn = dyn FnMut() -> Option; + /// The inner type to be used in the GObject type system. #[derive(Default)] pub struct ItemWidget { @@ -88,6 +131,8 @@ mod imp { pub conn_label: RefCell