@@ -194,11 +194,7 @@ pub fn format_usb_diag(diag: &protocol::UsbDiag, persona: protocol::Persona) ->
194194 let _ = writeln ! (
195195 out,
196196 " USB: {}{} mounts={} unmounts={} suspends={} resumes={}" ,
197- if diag. mounted( ) {
198- "configured"
199- } else {
200- "not configured"
201- } ,
197+ usb_state_label( diag) ,
202198 if diag. suspended( ) { " / suspended" } else { "" } ,
203199 diag. mount_count,
204200 diag. umount_count,
@@ -252,13 +248,28 @@ pub fn format_usb_diag(diag: &protocol::UsbDiag, persona: protocol::Persona) ->
252248}
253249
254250fn usb_verdict ( diag : & protocol:: UsbDiag , device_label : & str ) -> String {
255- if !diag. mounted ( ) {
256- if diag. device_desc_count > 0 || diag. config_desc_count > 0 {
257- format ! ( "FAIL USB host started enumeration but did not configure the {device_label} device." )
258- } else {
259- "FAIL Pico sees no USB host enumeration traffic." . to_string ( )
251+ match diag. configuration_state ( ) {
252+ protocol:: UsbConfigurationState :: NoHostTraffic => {
253+ return "FAIL Pico sees no USB host enumeration traffic." . to_string ( ) ;
254+ }
255+ protocol:: UsbConfigurationState :: EnumerationStarted => {
256+ return format ! ( "FAIL USB host started enumeration but did not configure the {device_label} device." ) ;
257+ }
258+ protocol:: UsbConfigurationState :: ConfiguredThenUnmountedWithoutCallback => {
259+ return format ! ( "FAIL USB configured the {device_label} device once, then current state is not mounted and no unmount callback was recorded." ) ;
260+ }
261+ protocol:: UsbConfigurationState :: ConfiguredThenUnmounted => {
262+ return format ! ( "FAIL USB configured the {device_label} device once, then the host disconnected or reset it." ) ;
263+ }
264+ protocol:: UsbConfigurationState :: Suspended => {
265+ return format ! (
266+ "WARN USB is suspended; the host has not resumed the {device_label} device."
267+ ) ;
260268 }
261- } else if !diag. xinput_report_sent ( ) {
269+ protocol:: UsbConfigurationState :: Configured => { }
270+ }
271+
272+ if !diag. xinput_report_sent ( ) {
262273 if diag. in_blocked_total ( ) > 0 && diag. last_in_blocked_reason != 0 {
263274 format ! (
264275 "WARN USB is configured, but the latest {device_label} report was blocked: {} (want={} got={})." ,
@@ -278,6 +289,19 @@ fn usb_verdict(diag: &protocol::UsbDiag, device_label: &str) -> String {
278289 }
279290}
280291
292+ fn usb_state_label ( diag : & protocol:: UsbDiag ) -> & ' static str {
293+ match diag. configuration_state ( ) {
294+ protocol:: UsbConfigurationState :: Configured => "configured" ,
295+ protocol:: UsbConfigurationState :: Suspended => "suspended" ,
296+ protocol:: UsbConfigurationState :: ConfiguredThenUnmounted
297+ | protocol:: UsbConfigurationState :: ConfiguredThenUnmountedWithoutCallback => {
298+ "not mounted now (configured earlier)"
299+ }
300+ protocol:: UsbConfigurationState :: EnumerationStarted
301+ | protocol:: UsbConfigurationState :: NoHostTraffic => "not configured" ,
302+ }
303+ }
304+
281305fn age_label ( diag : & protocol:: UsbDiag , timestamp_ms : u32 ) -> String {
282306 match diag. age_ms ( timestamp_ms) {
283307 Some ( ms) if ms < 1000 => format ! ( "{ms} ms ago" ) ,
@@ -358,6 +382,32 @@ mod tests {
358382 assert ! ( usb_verdict( & diag( true , true , true , true ) , "XInput" ) . contains( "OUT traffic" ) ) ;
359383 }
360384
385+ #[ test]
386+ fn verdict_distinguishes_configured_then_unmounted ( ) {
387+ let mut d = diag ( false , false , false , true ) ;
388+ d. mount_count = 1 ;
389+ d. umount_count = 0 ;
390+
391+ let verdict = usb_verdict ( & d, "PS3 HID gamepad" ) ;
392+ assert ! (
393+ verdict. contains( "configured the PS3 HID gamepad device once" ) ,
394+ "wrong verdict: {verdict}"
395+ ) ;
396+ assert ! (
397+ verdict. contains( "no unmount callback" ) ,
398+ "missing callback clue: {verdict}"
399+ ) ;
400+ assert ! (
401+ !verdict. contains( "did not configure" ) ,
402+ "kept old verdict: {verdict}"
403+ ) ;
404+ let text = format_usb_diag ( & d, protocol:: Persona :: Ps3 ) ;
405+ assert ! (
406+ text. contains( "USB: not mounted now (configured earlier)" ) ,
407+ "wrong USB state: {text}"
408+ ) ;
409+ }
410+
361411 #[ test]
362412 fn verdict_uses_persona_label ( ) {
363413 let warn = usb_verdict ( & diag ( true , false , false , true ) , "HID keyboard" ) ;
0 commit comments