#[macro_use]
extern crate bitflags;
extern crate pcsc_sys as ffi;
use std::ffi::{CStr, CString};
use std::mem::{forget, transmute};
use std::ops::Deref;
use std::os::raw::c_char;
use std::ptr::{null, null_mut};
use std::sync::Arc;
use ffi::{DWORD, LONG};
const DUMMY_LONG: LONG = -1;
const DUMMY_DWORD: DWORD = 0xdead_beef;
bitflags! {
pub struct State: DWORD {
const UNAWARE = ffi::SCARD_STATE_UNAWARE;
const IGNORE = ffi::SCARD_STATE_IGNORE;
const CHANGED = ffi::SCARD_STATE_CHANGED;
const UNKNOWN = ffi::SCARD_STATE_UNKNOWN;
const UNAVAILABLE = ffi::SCARD_STATE_UNAVAILABLE;
const EMPTY = ffi::SCARD_STATE_EMPTY;
const PRESENT = ffi::SCARD_STATE_PRESENT;
const ATRMATCH = ffi::SCARD_STATE_ATRMATCH;
const EXCLUSIVE = ffi::SCARD_STATE_EXCLUSIVE;
const INUSE = ffi::SCARD_STATE_INUSE;
const MUTE = ffi::SCARD_STATE_MUTE;
const UNPOWERED = ffi::SCARD_STATE_UNPOWERED;
}
}
bitflags! {
pub struct Status: DWORD {
const UNKNOWN = ffi::SCARD_UNKNOWN;
const ABSENT = ffi::SCARD_ABSENT;
const PRESENT = ffi::SCARD_PRESENT;
const SWALLOWED = ffi::SCARD_SWALLOWED;
const POWERED = ffi::SCARD_POWERED;
const NEGOTIABLE = ffi::SCARD_NEGOTIABLE;
const SPECIFIC = ffi::SCARD_SPECIFIC;
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ShareMode {
Exclusive = ffi::SCARD_SHARE_EXCLUSIVE as u32,
Shared = ffi::SCARD_SHARE_SHARED as u32,
Direct = ffi::SCARD_SHARE_DIRECT as u32,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Protocol {
T0 = ffi::SCARD_PROTOCOL_T0 as u32,
T1 = ffi::SCARD_PROTOCOL_T1 as u32,
RAW = ffi::SCARD_PROTOCOL_RAW as u32,
}
impl Protocol {
fn from_raw(raw: DWORD) -> Protocol {
match raw {
ffi::SCARD_PROTOCOL_T0 => Protocol::T0,
ffi::SCARD_PROTOCOL_T1 => Protocol::T1,
ffi::SCARD_PROTOCOL_RAW => Protocol::RAW,
_ => panic!("impossible protocol: {:#x}", raw),
}
}
}
bitflags! {
pub struct Protocols: DWORD {
const UNDEFINED = ffi::SCARD_PROTOCOL_UNDEFINED;
const T0 = ffi::SCARD_PROTOCOL_T0;
const T1 = ffi::SCARD_PROTOCOL_T1;
const RAW = ffi::SCARD_PROTOCOL_RAW;
const ANY = ffi::SCARD_PROTOCOL_ANY;
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Disposition {
LeaveCard = ffi::SCARD_LEAVE_CARD as u32,
ResetCard = ffi::SCARD_RESET_CARD as u32,
UnpowerCard = ffi::SCARD_UNPOWER_CARD as u32,
EjectCard = ffi::SCARD_EJECT_CARD as u32,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Error {
InternalError = ffi::SCARD_F_INTERNAL_ERROR as u32,
Cancelled = ffi::SCARD_E_CANCELLED as u32,
InvalidHandle = ffi::SCARD_E_INVALID_HANDLE as u32,
InvalidParameter = ffi::SCARD_E_INVALID_PARAMETER as u32,
InvalidTarget = ffi::SCARD_E_INVALID_TARGET as u32,
NoMemory = ffi::SCARD_E_NO_MEMORY as u32,
WaitedTooLong = ffi::SCARD_F_WAITED_TOO_LONG as u32,
InsufficientBuffer = ffi::SCARD_E_INSUFFICIENT_BUFFER as u32,
UnknownReader = ffi::SCARD_E_UNKNOWN_READER as u32,
Timeout = ffi::SCARD_E_TIMEOUT as u32,
SharingViolation = ffi::SCARD_E_SHARING_VIOLATION as u32,
NoSmartcard = ffi::SCARD_E_NO_SMARTCARD as u32,
UnknownCard = ffi::SCARD_E_UNKNOWN_CARD as u32,
CantDispose = ffi::SCARD_E_CANT_DISPOSE as u32,
ProtoMismatch = ffi::SCARD_E_PROTO_MISMATCH as u32,
NotReady = ffi::SCARD_E_NOT_READY as u32,
InvalidValue = ffi::SCARD_E_INVALID_VALUE as u32,
SystemCancelled = ffi::SCARD_E_SYSTEM_CANCELLED as u32,
CommError = ffi::SCARD_F_COMM_ERROR as u32,
UnknownError = ffi::SCARD_F_UNKNOWN_ERROR as u32,
InvalidAtr = ffi::SCARD_E_INVALID_ATR as u32,
NotTransacted = ffi::SCARD_E_NOT_TRANSACTED as u32,
ReaderUnavailable = ffi::SCARD_E_READER_UNAVAILABLE as u32,
Shutdown = ffi::SCARD_P_SHUTDOWN as u32,
PciTooSmall = ffi::SCARD_E_PCI_TOO_SMALL as u32,
ReaderUnsupported = ffi::SCARD_E_READER_UNSUPPORTED as u32,
DuplicateReader = ffi::SCARD_E_DUPLICATE_READER as u32,
CardUnsupported = ffi::SCARD_E_CARD_UNSUPPORTED as u32,
NoService = ffi::SCARD_E_NO_SERVICE as u32,
ServiceStopped = ffi::SCARD_E_SERVICE_STOPPED as u32,
#[cfg(target_os = "windows")]
Unexpected = ffi::SCARD_E_UNEXPECTED as u32,
IccInstallation = ffi::SCARD_E_ICC_INSTALLATION as u32,
IccCreateorder = ffi::SCARD_E_ICC_CREATEORDER as u32,
UnsupportedFeature = ffi::SCARD_E_UNSUPPORTED_FEATURE as u32,
DirNotFound = ffi::SCARD_E_DIR_NOT_FOUND as u32,
FileNotFound = ffi::SCARD_E_FILE_NOT_FOUND as u32,
NoDir = ffi::SCARD_E_NO_DIR as u32,
NoFile = ffi::SCARD_E_NO_FILE as u32,
NoAccess = ffi::SCARD_E_NO_ACCESS as u32,
WriteTooMany = ffi::SCARD_E_WRITE_TOO_MANY as u32,
BadSeek = ffi::SCARD_E_BAD_SEEK as u32,
InvalidChv = ffi::SCARD_E_INVALID_CHV as u32,
UnknownResMng = ffi::SCARD_E_UNKNOWN_RES_MNG as u32,
NoSuchCertificate = ffi::SCARD_E_NO_SUCH_CERTIFICATE as u32,
CertificateUnavailable = ffi::SCARD_E_CERTIFICATE_UNAVAILABLE as u32,
NoReadersAvailable = ffi::SCARD_E_NO_READERS_AVAILABLE as u32,
CommDataLost = ffi::SCARD_E_COMM_DATA_LOST as u32,
NoKeyContainer = ffi::SCARD_E_NO_KEY_CONTAINER as u32,
ServerTooBusy = ffi::SCARD_E_SERVER_TOO_BUSY as u32,
UnsupportedCard = ffi::SCARD_W_UNSUPPORTED_CARD as u32,
UnresponsiveCard = ffi::SCARD_W_UNRESPONSIVE_CARD as u32,
UnpoweredCard = ffi::SCARD_W_UNPOWERED_CARD as u32,
ResetCard = ffi::SCARD_W_RESET_CARD as u32,
RemovedCard = ffi::SCARD_W_REMOVED_CARD as u32,
SecurityViolation = ffi::SCARD_W_SECURITY_VIOLATION as u32,
WrongChv = ffi::SCARD_W_WRONG_CHV as u32,
ChvBlocked = ffi::SCARD_W_CHV_BLOCKED as u32,
Eof = ffi::SCARD_W_EOF as u32,
CancelledByUser = ffi::SCARD_W_CANCELLED_BY_USER as u32,
CardNotAuthenticated = ffi::SCARD_W_CARD_NOT_AUTHENTICATED as u32,
CacheItemNotFound = ffi::SCARD_W_CACHE_ITEM_NOT_FOUND as u32,
CacheItemStale = ffi::SCARD_W_CACHE_ITEM_STALE as u32,
CacheItemTooBig = ffi::SCARD_W_CACHE_ITEM_TOO_BIG as u32,
}
impl Error {
fn from_raw(raw: LONG) -> Error {
unsafe {
if ffi::SCARD_F_INTERNAL_ERROR <= raw && raw <= ffi::SCARD_E_SERVER_TOO_BUSY
|| ffi::SCARD_W_UNSUPPORTED_CARD <= raw && raw <= ffi::SCARD_W_CACHE_ITEM_TOO_BIG
{
transmute::<u32, Error>(raw as u32)
} else {
debug_assert!(false, format!("unknown PCSC error code: {:#x}", raw));
Error::UnknownError
}
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::InternalError => "An internal consistency check failed",
Error::Cancelled => "The action was cancelled by an SCardCancel request",
Error::InvalidHandle => "The supplied handle was invalid",
Error::InvalidParameter => "One or more of the supplied parameters could not be properly interpreted",
Error::InvalidTarget => "Registry startup information is missing or invalid",
Error::NoMemory => "Not enough memory available to complete this command",
Error::WaitedTooLong => "An internal consistency timer has expired",
Error::InsufficientBuffer => "The data buffer to receive returned data is too small for the returned data",
Error::UnknownReader => "The specified reader name is not recognized",
Error::Timeout => "The user-specified timeout value has expired",
Error::SharingViolation => "The smart card cannot be accessed because of other connections outstanding",
Error::NoSmartcard => "The operation requires a Smart Card, but no Smart Card is currently in the device",
Error::UnknownCard => "The specified smart card name is not recognized",
Error::CantDispose => "The system could not dispose of the media in the requested manner",
Error::ProtoMismatch => "The requested protocols are incompatible with the protocol currently in use with the smart card",
Error::NotReady => "The reader or smart card is not ready to accept commands",
Error::InvalidValue => "One or more of the supplied parameters values could not be properly interpreted",
Error::SystemCancelled => "The action was cancelled by the system, presumably to log off or shut down",
Error::CommError => "An internal communications error has been detected",
Error::UnknownError => "An internal error has been detected, but the source is unknown",
Error::InvalidAtr => "An ATR obtained from the registry is not a valid ATR string",
Error::NotTransacted => "An attempt was made to end a non-existent transaction",
Error::ReaderUnavailable => "The specified reader is not currently available for use",
Error::Shutdown => "The operation has been aborted to allow the server application to exit",
Error::PciTooSmall => "The PCI Receive buffer was too small",
Error::ReaderUnsupported => "The reader driver does not meet minimal requirements for support",
Error::DuplicateReader => "The reader driver did not produce a unique reader name",
Error::CardUnsupported => "The smart card does not meet minimal requirements for support",
Error::NoService => "The Smart card resource manager is not running",
Error::ServiceStopped => "The Smart card resource manager has shut down",
#[cfg(target_os = "windows")]
Error::Unexpected => "An unexpected card error has occurred",
Error::UnsupportedFeature => "This smart card does not support the requested feature",
Error::IccInstallation => "No primary provider can be found for the smart card",
Error::IccCreateorder => "The requested order of object creation is not supported",
Error::DirNotFound => "The identified directory does not exist in the smart card",
Error::FileNotFound => "The identified file does not exist in the smart card",
Error::NoDir => "The supplied path does not represent a smart card directory",
Error::NoFile => "The supplied path does not represent a smart card file",
Error::NoAccess => "Access is denied to this file",
Error::WriteTooMany => "The smart card does not have enough memory to store the information",
Error::BadSeek => "There was an error trying to set the smart card file object pointer",
Error::InvalidChv => "The supplied PIN is incorrect",
Error::UnknownResMng => "An unrecognized error code was returned from a layered component",
Error::NoSuchCertificate => "The requested certificate does not exist",
Error::CertificateUnavailable => "The requested certificate could not be obtained",
Error::NoReadersAvailable => "Cannot find a smart card reader",
Error::CommDataLost => "A communications error with the smart card has been detected. Retry the operation",
Error::NoKeyContainer => "The requested key container does not exist on the smart card",
Error::ServerTooBusy => "The smart card resource manager is too busy to complete this operation",
Error::UnsupportedCard => "The reader cannot communicate with the card, due to ATR string configuration conflicts",
Error::UnresponsiveCard => "The smart card is not responding to a reset",
Error::UnpoweredCard => "Power has been removed from the smart card, so that further communication is not possible",
Error::ResetCard => "The smart card has been reset, so any shared state information is invalid",
Error::RemovedCard => "The smart card has been removed, so further communication is not possible",
Error::SecurityViolation => "Access was denied because of a security violation",
Error::WrongChv => "The card cannot be accessed because the wrong PIN was presented",
Error::ChvBlocked => "The card cannot be accessed because the maximum number of PIN entry attempts has been reached",
Error::Eof => "The end of the smart card file has been reached",
Error::CancelledByUser => r#"The user pressed "Cancel" on a Smart Card Selection Dialog"#,
Error::CardNotAuthenticated => "No PIN was presented to the smart card",
Error::CacheItemNotFound => "The requested item could not be found in the cache",
Error::CacheItemStale => "The requested cache item is too old and was deleted from the cache",
Error::CacheItemTooBig => "The new cache item exceeds the maximum per-item size defined for the cache",
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.write_str(std::error::Error::description(self))
}
}
macro_rules! try_pcsc {
($e:expr) => (match $e {
ffi::SCARD_S_SUCCESS => (),
err => return Err(Error::from_raw(err)),
});
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Scope {
User = ffi::SCARD_SCOPE_USER as u32,
Terminal = ffi::SCARD_SCOPE_TERMINAL as u32,
System = ffi::SCARD_SCOPE_SYSTEM as u32,
Global = ffi::SCARD_SCOPE_GLOBAL as u32,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AttributeClass {
VendorInfo = ffi::SCARD_CLASS_VENDOR_INFO as u32,
Communications = ffi::SCARD_CLASS_COMMUNICATIONS as u32,
Protocol = ffi::SCARD_CLASS_PROTOCOL as u32,
PowerMgmt = ffi::SCARD_CLASS_POWER_MGMT as u32,
Security = ffi::SCARD_CLASS_SECURITY as u32,
Mechanical = ffi::SCARD_CLASS_MECHANICAL as u32,
VendorDefined = ffi::SCARD_CLASS_VENDOR_DEFINED as u32,
IfdProtocol = ffi::SCARD_CLASS_IFD_PROTOCOL as u32,
IccState = ffi::SCARD_CLASS_ICC_STATE as u32,
System = ffi::SCARD_CLASS_SYSTEM as u32,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Attribute {
VendorName = ffi::SCARD_ATTR_VENDOR_NAME as u32,
VendorIfdType = ffi::SCARD_ATTR_VENDOR_IFD_TYPE as u32,
VendorIfdVersion = ffi::SCARD_ATTR_VENDOR_IFD_VERSION as u32,
VendorIfdSerialNo = ffi::SCARD_ATTR_VENDOR_IFD_SERIAL_NO as u32,
ChannelId = ffi::SCARD_ATTR_CHANNEL_ID as u32,
AsyncProtocolTypes = ffi::SCARD_ATTR_ASYNC_PROTOCOL_TYPES as u32,
DefaultClk = ffi::SCARD_ATTR_DEFAULT_CLK as u32,
MaxClk = ffi::SCARD_ATTR_MAX_CLK as u32,
DefaultDataRate = ffi::SCARD_ATTR_DEFAULT_DATA_RATE as u32,
MaxDataRate = ffi::SCARD_ATTR_MAX_DATA_RATE as u32,
MaxIfsd = ffi::SCARD_ATTR_MAX_IFSD as u32,
SyncProtocolTypes = ffi::SCARD_ATTR_SYNC_PROTOCOL_TYPES as u32,
PowerMgmtSupport = ffi::SCARD_ATTR_POWER_MGMT_SUPPORT as u32,
UserToCardAuthDevice = ffi::SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE as u32,
UserAuthInputDevice = ffi::SCARD_ATTR_USER_AUTH_INPUT_DEVICE as u32,
Characteristics = ffi::SCARD_ATTR_CHARACTERISTICS as u32,
CurrentProtocolType = ffi::SCARD_ATTR_CURRENT_PROTOCOL_TYPE as u32,
CurrentClk = ffi::SCARD_ATTR_CURRENT_CLK as u32,
CurrentF = ffi::SCARD_ATTR_CURRENT_F as u32,
CurrentD = ffi::SCARD_ATTR_CURRENT_D as u32,
CurrentN = ffi::SCARD_ATTR_CURRENT_N as u32,
CurrentW = ffi::SCARD_ATTR_CURRENT_W as u32,
CurrentIfsc = ffi::SCARD_ATTR_CURRENT_IFSC as u32,
CurrentIfsd = ffi::SCARD_ATTR_CURRENT_IFSD as u32,
CurrentBwt = ffi::SCARD_ATTR_CURRENT_BWT as u32,
CurrentCwt = ffi::SCARD_ATTR_CURRENT_CWT as u32,
CurrentEbcEncoding = ffi::SCARD_ATTR_CURRENT_EBC_ENCODING as u32,
ExtendedBwt = ffi::SCARD_ATTR_EXTENDED_BWT as u32,
IccPresence = ffi::SCARD_ATTR_ICC_PRESENCE as u32,
IccInterfaceStatus = ffi::SCARD_ATTR_ICC_INTERFACE_STATUS as u32,
CurrentIoState = ffi::SCARD_ATTR_CURRENT_IO_STATE as u32,
AtrString = ffi::SCARD_ATTR_ATR_STRING as u32,
IccTypePerAtr = ffi::SCARD_ATTR_ICC_TYPE_PER_ATR as u32,
EscReset = ffi::SCARD_ATTR_ESC_RESET as u32,
EscCancel = ffi::SCARD_ATTR_ESC_CANCEL as u32,
EscAuthrequest = ffi::SCARD_ATTR_ESC_AUTHREQUEST as u32,
Maxinput = ffi::SCARD_ATTR_MAXINPUT as u32,
DeviceUnit = ffi::SCARD_ATTR_DEVICE_UNIT as u32,
DeviceInUse = ffi::SCARD_ATTR_DEVICE_IN_USE as u32,
DeviceFriendlyName = ffi::SCARD_ATTR_DEVICE_FRIENDLY_NAME as u32,
DeviceSystemName = ffi::SCARD_ATTR_DEVICE_SYSTEM_NAME as u32,
SupressT1IfsRequest = ffi::SCARD_ATTR_SUPRESS_T1_IFS_REQUEST as u32,
}
pub const MAX_ATR_SIZE: usize = ffi::MAX_ATR_SIZE;
pub const MAX_BUFFER_SIZE: usize = ffi::MAX_BUFFER_SIZE;
pub const MAX_BUFFER_SIZE_EXTENDED: usize = ffi::MAX_BUFFER_SIZE_EXTENDED;
#[allow(non_snake_case)]
pub fn PNP_NOTIFICATION() -> &'static CStr {
unsafe { CStr::from_bytes_with_nul_unchecked(b"\\\\?PnP?\\Notification\0") }
}
#[repr(C)]
pub struct ReaderState {
inner: ffi::SCARD_READERSTATE,
}
fn get_protocol_pci(protocol: Protocol) -> &'static ffi::SCARD_IO_REQUEST {
unsafe {
match protocol {
Protocol::T0 => &ffi::g_rgSCardT0Pci,
Protocol::T1 => &ffi::g_rgSCardT1Pci,
Protocol::RAW => &ffi::g_rgSCardRawPci,
}
}
}
struct ContextInner {
handle: ffi::SCARDCONTEXT,
}
pub struct Context {
inner: Arc<ContextInner>,
}
pub struct Card {
_context: Context,
handle: ffi::SCARDHANDLE,
active_protocol: Protocol,
}
pub struct Transaction<'tx> {
card: &'tx mut Card,
}
#[derive(Clone)]
pub struct ReaderNames<'buf> {
buf: &'buf [u8],
pos: usize,
}
impl<'buf> Iterator for ReaderNames<'buf> {
type Item = &'buf CStr;
fn next(&mut self) -> Option<&'buf CStr> {
match self.buf[self.pos..].iter().position(|&c| c == 0) {
None | Some(0) => None,
Some(len) => {
let old_pos = self.pos;
self.pos += len + 1;
Some(CStr::from_bytes_with_nul(&self.buf[old_pos..self.pos]).unwrap())
}
}
}
}
impl Context {
pub fn establish(
scope: Scope,
) -> Result<Context, Error> {
unsafe {
let mut handle: ffi::SCARDCONTEXT = DUMMY_LONG as ffi::SCARDCONTEXT;
try_pcsc!(ffi::SCardEstablishContext(
scope as DWORD,
null(),
null(),
&mut handle,
));
Ok(Context {
inner: Arc::new(ContextInner {
handle,
}),
})
}
}
pub fn release(
self
) -> Result<(), (Context, Error)> {
match Arc::try_unwrap(self.inner) {
Ok(inner) => {
unsafe {
let err = ffi::SCardReleaseContext(
inner.handle,
);
if err != ffi::SCARD_S_SUCCESS {
let context = Context { inner: Arc::new(inner) };
return Err((context, Error::from_raw(err)));
}
forget(inner);
Ok(())
}
},
Err(arc_inner) => {
let context = Context { inner: arc_inner };
Err((context, Error::CantDispose))
}
}
}
pub fn is_valid(
&self
) -> Result<(), Error> {
unsafe {
try_pcsc!(ffi::SCardIsValidContext(
self.inner.handle,
));
Ok(())
}
}
pub fn cancel(
&self,
) -> Result<(), Error> {
unsafe {
try_pcsc!(ffi::SCardCancel(
self.inner.handle,
));
Ok(())
}
}
pub fn list_readers<'buf>(
&self,
buffer: &'buf mut [u8],
) -> Result<ReaderNames<'buf>, Error> {
unsafe {
let mut buflen = buffer.len() as DWORD;
let err = ffi::SCardListReaders(
self.inner.handle,
null(),
buffer.as_mut_ptr() as *mut c_char,
&mut buflen,
);
if err == Error::NoReadersAvailable as LONG {
return Ok(ReaderNames {
buf: b"\0",
pos: 0,
});
}
if err != ffi::SCARD_S_SUCCESS {
return Err(Error::from_raw(err));
}
Ok(ReaderNames {
buf: &buffer[..buflen as usize],
pos: 0,
})
}
}
pub fn list_readers_len(
&self,
) -> Result<usize, Error> {
unsafe {
let mut buflen = DUMMY_DWORD;
let err = ffi::SCardListReaders(
self.inner.handle,
null(),
null_mut(),
&mut buflen,
);
if err == Error::NoReadersAvailable as LONG {
return Ok(0);
}
if err != ffi::SCARD_S_SUCCESS {
return Err(Error::from_raw(err));
}
Ok(buflen as usize)
}
}
pub fn connect(
&self,
reader: &CStr,
share_mode: ShareMode,
preferred_protocols: Protocols,
) -> Result<Card, Error> {
unsafe {
let mut handle: ffi::SCARDHANDLE = DUMMY_LONG as ffi::SCARDHANDLE;
let mut raw_active_protocol: DWORD = DUMMY_DWORD;
try_pcsc!(ffi::SCardConnect(
self.inner.handle,
reader.as_ptr(),
share_mode as DWORD,
preferred_protocols.bits(),
&mut handle,
&mut raw_active_protocol,
));
let active_protocol = Protocol::from_raw(raw_active_protocol);
Ok(Card {
_context: self.clone(),
handle,
active_protocol,
})
}
}
pub fn get_status_change<D>(
&self,
timeout: D,
readers: &mut [ReaderState],
) -> Result<(), Error>
where D: Into<Option<std::time::Duration>> {
let timeout_ms = match timeout.into() {
Some(duration) => {
let timeout_ms_u64 = duration.as_secs()
.saturating_mul(1000)
.saturating_add(u64::from(duration.subsec_nanos()) / 1_000_000);
std::cmp::min(ffi::INFINITE, timeout_ms_u64 as DWORD)
},
None => ffi::INFINITE
};
unsafe {
try_pcsc!(ffi::SCardGetStatusChange(
self.inner.handle,
timeout_ms,
readers.as_mut_ptr() as *mut ffi::SCARD_READERSTATE,
readers.len() as DWORD,
));
Ok(())
}
}
}
impl Drop for ContextInner {
fn drop(&mut self) {
unsafe {
let _err = ffi::SCardReleaseContext(
self.handle,
);
}
}
}
impl Clone for Context {
fn clone(&self) -> Self {
Context {
inner: Arc::clone(&self.inner),
}
}
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
impl ReaderState {
pub fn new<T: Into<CString>>(
name: T,
current_state: State,
) -> ReaderState {
ReaderState {
inner: ffi::SCARD_READERSTATE {
szReader: name.into().into_raw(),
pvUserData: null_mut(),
dwCurrentState: current_state.bits(),
dwEventState: State::UNAWARE.bits(),
cbAtr: 0,
rgbAtr: [0; ffi::ATR_BUFFER_SIZE],
},
}
}
pub fn name(&self) -> &CStr {
unsafe { CStr::from_ptr(self.inner.szReader) }
}
pub fn atr(&self) -> &[u8] {
&self.inner.rgbAtr[0..self.inner.cbAtr as usize]
}
pub fn event_state(&self) -> State {
State::from_bits_truncate(self.inner.dwEventState)
}
pub fn event_count(&self) -> u32 {
((self.inner.dwEventState & 0xFFFF_0000) >> 16) as u32
}
pub fn sync_current_state(&mut self) {
self.inner.dwCurrentState = self.inner.dwEventState;
}
}
impl Drop for ReaderState {
fn drop(&mut self) {
unsafe { CString::from_raw(self.inner.szReader as *mut c_char) };
}
}
impl Card {
pub fn transaction(
&mut self,
) -> Result<Transaction, Error> {
unsafe {
try_pcsc!(ffi::SCardBeginTransaction(
self.handle,
));
Ok(Transaction {
card: self,
})
}
}
pub fn reconnect(
&mut self,
share_mode: ShareMode,
preferred_protocols: Protocols,
initialization: Disposition,
) -> Result<(), Error> {
unsafe {
let mut raw_active_protocol: DWORD = DUMMY_DWORD;
try_pcsc!(ffi::SCardReconnect(
self.handle,
share_mode as DWORD,
preferred_protocols.bits(),
initialization as DWORD,
&mut raw_active_protocol,
));
self.active_protocol = Protocol::from_raw(raw_active_protocol);
Ok(())
}
}
pub fn disconnect(
self,
disposition: Disposition,
) -> Result<(), (Card, Error)> {
unsafe {
let err = ffi::SCardDisconnect(
self.handle,
disposition as DWORD,
);
if err != ffi::SCARD_S_SUCCESS {
return Err((self, Error::from_raw(err)));
}
forget(self);
Ok(())
}
}
pub fn status(
&self,
) -> Result<(Status, Protocol), Error> {
unsafe {
let mut raw_status: DWORD = DUMMY_DWORD;
let mut raw_protocol: DWORD = DUMMY_DWORD;
try_pcsc!(ffi::SCardStatus(
self.handle,
null_mut(),
null_mut(),
&mut raw_status,
&mut raw_protocol,
null_mut(),
null_mut(),
));
let status = Status::from_bits_truncate(raw_status);
let protocol = Protocol::from_raw(raw_protocol);
Ok((status, protocol))
}
}
pub fn get_attribute<'buf>(
&self,
attribute: Attribute,
buffer: &'buf mut [u8],
) -> Result<&'buf [u8], Error> {
unsafe {
let mut attribute_len = buffer.len() as DWORD;
try_pcsc!(ffi::SCardGetAttrib(
self.handle,
attribute as DWORD,
buffer.as_mut_ptr(),
&mut attribute_len,
));
Ok(&buffer[0..attribute_len as usize])
}
}
pub fn get_attribute_len(
&self,
attribute: Attribute,
) -> Result<usize, Error> {
unsafe {
let mut attribute_len = DUMMY_DWORD;
try_pcsc!(ffi::SCardGetAttrib(
self.handle,
attribute as DWORD,
null_mut(),
&mut attribute_len,
));
Ok(attribute_len as usize)
}
}
pub fn set_attribute(
&self,
attribute: Attribute,
attribute_data: &[u8],
) -> Result<(), Error> {
unsafe {
try_pcsc!(ffi::SCardSetAttrib(
self.handle,
attribute as DWORD,
attribute_data.as_ptr(),
attribute_data.len() as DWORD,
));
Ok(())
}
}
pub fn transmit<'buf>(
&self,
send_buffer: &[u8],
receive_buffer: &'buf mut [u8],
) -> Result<&'buf [u8], Error> {
let send_pci = get_protocol_pci(self.active_protocol);
let recv_pci = null_mut();
let mut receive_len = receive_buffer.len() as DWORD;
unsafe {
try_pcsc!(ffi::SCardTransmit(
self.handle,
send_pci,
send_buffer.as_ptr(),
send_buffer.len() as DWORD,
recv_pci,
receive_buffer.as_mut_ptr(),
&mut receive_len,
));
Ok(&receive_buffer[0..receive_len as usize])
}
}
pub fn control<'buf>(
&self,
control_code: DWORD,
send_buffer: &[u8],
receive_buffer: &'buf mut [u8],
) -> Result<&'buf [u8], Error> {
let mut receive_len: DWORD = DUMMY_DWORD;
unsafe {
try_pcsc!(ffi::SCardControl(
self.handle,
control_code,
send_buffer.as_ptr(),
send_buffer.len() as DWORD,
receive_buffer.as_mut_ptr(),
receive_buffer.len() as DWORD,
&mut receive_len,
));
Ok(&receive_buffer[0..receive_len as usize])
}
}
}
impl Drop for Card {
fn drop(&mut self) {
unsafe {
let _err = ffi::SCardDisconnect(
self.handle,
Disposition::ResetCard as DWORD,
);
}
}
}
unsafe impl Send for Card {}
unsafe impl Sync for Card {}
impl<'tx> Transaction<'tx> {
pub fn end(
self,
disposition: Disposition,
) -> Result<(), (Transaction<'tx>, Error)> {
unsafe {
let err = ffi::SCardEndTransaction(
self.card.handle,
disposition as DWORD,
);
if err != 0 {
return Err((self, Error::from_raw(err)));
}
forget(self);
Ok(())
}
}
}
impl<'tx> Drop for Transaction<'tx> {
fn drop(&mut self) {
unsafe {
let _err = ffi::SCardEndTransaction(
self.card.handle,
Disposition::LeaveCard as DWORD,
);
}
}
}
impl<'tx> Deref for Transaction<'tx> {
type Target = Card;
fn deref(&self) -> &Card {
self.card
}
}