1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// cargo run --bin pcsc_diag
use pcsc::{Context, ReaderState, Scope, State, Error};
use std::{ffi::CString, time::Duration, thread};
fn main() {
// make all panics and logs visible
std::panic::set_hook(Box::new(|info| eprintln!("[PANIC] {info}")));
eprintln!("[diag] starting…");
// 1) establish context
let ctx = match Context::establish(Scope::User) {
Ok(c) => {
eprintln!("[diag] Context established.");
c
}
Err(e) => {
eprintln!("[diag] Context establish FAILED: {e:?}");
return;
}
};
// 2) persistent reader name buffer (must outlive the iterator)
let mut readers_buf = [0u8; 2048];
// 3) main loop (prove we enter it)
let mut tick: u64 = 0;
loop {
tick += 1;
if tick % 10 == 0 {
eprintln!("[diag] heartbeat tick={tick}");
}
// 3a) list readers
match ctx.list_readers(&mut readers_buf) {
Ok(names) => {
let readers: Vec<_> = names.map(|n| n.to_string_lossy().into_owned()).collect();
if readers.is_empty() {
eprintln!("[diag] No readers attached. Sleeping…");
thread::sleep(Duration::from_millis(500));
continue;
}
eprintln!("[diag] Readers: {:?}", readers);
// 3b) build ReaderState structs from names (UNAWARE on first pass)
let mut states: Vec<ReaderState> = readers.iter()
.map(|name| {
let c = CString::new(name.as_str()).expect("CString::new");
ReaderState::new(c, State::UNAWARE)
})
.collect();
// 3c) wait for status change (or timeout)
match ctx.get_status_change(Some(Duration::from_secs(1)), &mut states) {
Ok(_) => {
for (i, st) in states.iter().enumerate() {
let ev = st.event_state();
let cur = st.current_state();
eprintln!("[diag] {} : event={:?} current={:?}",
readers[i], ev, cur);
// example: connect when PRESENT & not INUSE
if ev.contains(State::PRESENT) && !ev.contains(State::INUSE) {
match ctx.connect(st.name(), pcsc::ShareMode::Shared, pcsc::Protocols::ANY) {
Ok(_card) => eprintln!("[diag] Connected to {}", readers[i]),
Err(e) => eprintln!("[diag] connect error on {}: {:?}", readers[i], e),
}
}
}
}
Err(e) if e == Error::Timeout
|| e == Error::Cancelled
|| e == Error::NoService => {
// benign under some conditions; keep looping
// Note: cppvsdbg often shows first-chance RPC exceptions here.
}
Err(e) => {
eprintln!("[diag] get_status_change ERROR: {e:?} (continuing)");
thread::sleep(Duration::from_millis(200));
}
}
}
Err(e) if e == Error::NoReadersAvailable => {
eprintln!("[diag] NoReadersAvailable (normal if nothing is plugged). Sleeping…");
thread::sleep(Duration::from_millis(500));
}
Err(e) => {
eprintln!("[diag] list_readers ERROR: {e:?} (continuing)");
thread::sleep(Duration::from_millis(500));
}
}
}
}