COMP3301 2022 Assignment 3
OpenBSD ccid(4) USB CCID Driver Enhancement
Due: 28th of October 2022, Week 13
$Revision: 429 $
1 OpenBSD ccid(4) USB CCID Driver Enhancement
The task in this assignment is to extend the ccid(4) driver written for Assignment 2, adding support for
concurrent access to smartcard slots and non-blocking event driven I/O.
The purpose of this assignment is to demonstrate your ability to read and comprehend technical specifications,
and apply low level C programming skills to an operating system kernel environment.
You will be provided with the USB CCID specification, a virtual USB device on the COMP3301 virtual machines,
and userland code to test your driver functionality.
This is an individual assignment. You should feel free to discuss aspects of C programming and the assignment
specification with fellow students, and discuss OpenBSD and its APIs in general terms. It is cheating to look at
another student’s code and it is cheating to allow your code to be seen or shared in printed or electronic form.
You should note that all submitted code will be subject to automated checks for plagiarism and collusion. If we
detect plagiarism or collusion (outside of the base code given to everyone), formal misconduct proceedings will be
initiated against you. If you’re having trouble, seek help from a member of the teaching staff. Don’t be tempted to
copy another student’s code. You should read and understand the statements on student misconduct in the course
profile and on the school web-site: https://www.itee.uq.edu.au/itee-student-misconduct-including-plagiarism
2 Overview
This assignment builds on Assignment 2, therefore background information about the USB CCID protocol and
smartcards applies here.
In Assignment 2 you built a basic working CCID driver that can be used by a single process. In this assignment
you will fill out the remaining functionality to make it into a more practical, useful driver for applications.
The core of this assignment is extending the ccid(4) driver featured in A2 to support multiple slots and
concurrent access using a cloneable character device.
Three additional features which build on this are:
Transactions;
Non-blocking I/O, with support for kqueue(2); and
Timeouts on CCID-level commands.
These three tasks do interrelate to some extent – e.g. transactions and non-blocking I/O overlap with a few
transaction-related ioctl() commands having non-blocking semantics. However, part marks will be given for
attempts at each of them on their own.
1
The specification for this assignment generally assumes you will be attempting all parts of the task, as a result
of this interdependence. If you decide to attempt only some parts of the task, there are some notes in the
specification about how behaviour may change, but mostly you will need to consider how an application can
logically use the resulting interface. Full marks for any one part will be difficult to achieve without attempting all
tasks.
2.1 Concurrent access
Assignment 2 specified that the device special file associated with a ccid(4) driver instance must support
exclusive access. The first open (i.e., open("/dev/ccid0", ...)) could succeed, but until the device was
closed all subsequent opens failed with EBUSY. This assignment changes the software specification to allow each
CCID reader device and slot to be opened multiple times, with extra functionality to coordinate access to the slot.
Talking to a smartcard via ccid(4) involves sending commands with write() and then getting results in a
separate syscall with read(). It is important that the read() and write() calls match up (i.e. that you read
in the result of the write command you just sent). Many application-level operations on smartcards are also
composed of multiple write()/read() pairs which only make sense when executed together in order.
With a single minor number and device special file per slot, if we allow multiple processes to open it, there will be
no reliable way to tell which write() or read() calls match with which process or set of operations, and no way
to tell if a process exits or closes their file descriptor without reading in results – all we get in our driver is the
same minor number from every process, and we only get one ccidclose() call after every file descriptor on the
system using that minor has been closed.
To rectify this, we will add both tracking of per-process state and also the ability for processes to notify the
kernel when they begin and send a sequence of operations that must be performed together (which we will refer
to as a “transaction”).
2.2 Transactions
A transaction in ccid(4) starts with a CCIDIOC_XACT_BEGIN ioctl() and ends either at a CCID_IOC_XACT_*_COMMIT
ioctl() or when the relevant file descriptor is closed (due to a close() syscall or process exit).
During a transaction, the smartcard is exclusively under the control of the process which opened the transaction
(no other process can send commands to the smartcard at the same time). Multiple commands may be sent and
their results received using read() and write() by the original process until the transaction ends.
At the end of the transaction, the device is either released for use by another process, or the transacting process
can indicate that the card should be reset or powered down. If the transaction is ended “uncleanly” (by close()
or process exit), the device is always reset.
This enables applications to be sure that even in the case where they crash in the middle of a sequence of
operations, no security state changes they make will be accidentally leaked to another process.
ccid(4) in this assignment will require a program to begin a transaction by making an ioctl() call before being
able to write any commands to the device.
2.3 VFS and vnodes
Device special file access is mediated by the virtual file system (vfs(9)) layer and the vnode(9) abstraction.
Every time a device special is opened, the VFS layer looks for an existing vnode and creates one if it doesn’t
already exist. The device special open handler (eg, ccidopen()) is called to validate the access. If that succeeds,
VFS increments a reference count on the vnode and the program gets access to the device via the file descriptor
returned by open(2). When a program closes the relevant file descriptor (fd) or exits, the vnode reference
count is decremented. Once the reference count on the vnode drops to 0 then the driver close handler (e.g.,
ccidclose()) is called.
2
This means that while your driver code will receive a ccidopen() call every time a given device minor is opened,
it will not receive a ccidclose() call every time one is closed – that call occurs only when the last file descriptor
open for that minor is closed.
Therefore, to enable the same slot to be opened multiple times, while also supporting implicit completion of the
transaction when the device is closed, ccid(4) cannot use a single minor number per driver instance.
Further, the only consistent identifier associated with an open of a device is the dev_t argument to the device
special handlers. It is possible for one program to try to open the one device multiple times and get separate file
descriptors referring to the same device. File descriptors are also inherited by programs when they call fork(2),
meaning multiple programs may have a reference to the same file descriptor. Both these situations mean the
current process ID or pointer cannot be used to safely identify which context is within a transaction. In the first
situation the same process may have multiple file descriptors open against the same device so you can’t tell which
is which, and in the second situation the process id can change without notifying the driver.
Therefore, ccid(4) must map multiple device special file minor numbers to one unit of hardware, rather than
the simple approach used in A2 where each minor mapped to one unit.
2.4 Cloneable devices
Traditionally, to open a device special file with a specific major and minor number, it must be present on a file
system (usually under /dev) and accessible to a program.
Therefore, if we decided that e.g. minor numbers 0-8 each correspond to one “slice” of slot 0, unit 0, then we
would have to create separate device special files for each (e.g. /dev/ccid0.0 => 101, 0, /dev/ccid0.1 =>
101, 1, /dev/ccid1.0 => 101, 8 etc). A program trying to access the hardware device would have to iterate
over all of the “slices” of the unit it wants to open, trying to find one that’s not busy with another process.
This is not unworkable, but it is ugly for application programmers to use. Creating lots of special files in /dev
also costs disk space on metadata, and makes administration more difficult (separate file permissions).
This is hardly a unique problem faced by ccid(4), however, and OpenBSD (like other operating systems) has
developed a “cloneable device” facility to make this scheme nicer to use. With a cloneable device, each open()
of a device special file automatically receives a new minor number, generated by the kernel. To use it, device
drivers set the D_CLONE flag in the glue code in src/sys/sys/conf.h.
When D_CLONE is enabled on a device, the kernel takes over a portion of the bits in the device minor number. A
driver (e.g., ccid(4)) is still able to use the rest of the bits of the minor number for its own purposes (e.g.,
identifying an instance of a hardware driver), but some are taken by the kernel to create separate vnodes and
dev_t values that the driver may use to differentiate between concurrent opens of the same resource.
The bits used by the kernel for dynamic minors are the high bits (most significant) of the minor number. The
number of bits left for drivers to use is identified by the CLONE_SHIFT macro in sys/specdev.h.
Currently in OpenBSD, CLONE_SHIFT is 8, which means that there are 8 bits left for the driver (leading to a
maximum of 256 hardware units for a cloneable device).
In this assignment, you will be converting ccid(4) to be a cloneable character device driver using this scheme.
2.5 Non-Blocking I/O
Traditionally, and by default, when a program performs I/O on a file descriptor the kernel may wait or sleep in
the syscall handler until the related operation makes progress. For example, if a program is fetching data off a
web server using a network socket it will perform a series of read() syscalls against the socket file descriptor. If
the server has not yet sent data, the kernel will de-schedule the calling program (put it to sleep) while it waits for
data from the server. Only when data has been received by the network socket will the kernel wake the waiting
3
program up and finish handling the read syscall. From the perspective of the program, this read syscall appears
to “block” further execution of the program until data has been received from the network.
If the program has other work it could do while waiting for data from the network server, it can configure the
file descriptor for non-blocking operation. In this situation, if the program attempts to read() from the socket
file descriptor when no data is available, the kernel will return immediately with an error, setting errno(2) to
EAGAIN. The program can detect this and move on to perform other work. When data does become available
later, a subsequent read will proceed as normal.
This relies on the kernel being able to receive data on behalf of the file descriptor outside the context of the read
syscall handler. Generally, this means the file descriptor has an associated buffer that can be filled in when the
data becomes available (from the network, or a USB device, etc). When the program then tries to read from the
file descriptor, it uses the buffered data.
Similarly, writes from a program can also block. A write() to a TCP network socket may block until the remote
end of the connection has received and acknowledged the the data. A non-blocking write may fill a buffer and
return immediately, with subsequent writes failing with EAGAIN until space in the buffer becomes available again.
The kernel is then responsible for transmitting data out of the buffer and emptying it when the remote end has
acknowledged the data.
For non-blocking read and write operations, the kernel is performing work associated with the file descriptor on
behalf of the program while the program is doing other work. When this associated work has completed (e.g., a
write buffer has space again, or a read buffer has data in it) the program likely also wants to be notified, so that
it can perform further writes or reads.
The standard UNIX facilities for handling this notification are the select(2) and poll(2) syscalls. In OpenBSD,
the select(2) system call is implemented as a wrapper around poll(2), and drivers such as ccid(4) only have
to implement a handler for poll.
These allow programs to check a large set of file descriptors at once for which (if any) are currently available for
read() or write(). They can also be configured to sleep (or block) until at least one of the set is available, or
a specified amount of time has elapsed.
When read buffer has been or is filled by the kernel, it reports that fact to the poll facility, which will (if necessary)
wake up the program and report which file descriptor has become readable.
This facility is often used by programs to enable event-driven I/O multiplexing across a large number of file
descriptors (e.g. a network server handling many clients at once, without needing a separate thread per client).
However, select(2) and poll(2) struggle to scale beyond a certain number of file descriptors or events due to
their requirement to read in the entire set of FDs every time they are called. Different operating systems provide
alternative (but non-standard) facilities to address this. OpenBSD, as a member of the BSD family of operating
systems, uses the kqueue event notification facility.
In OpenBSD 7.1 and later, the select(2) and poll(2) system calls are internally implemented on top of the
kernel facilities for kqueue(2). This means that drivers only have to implement their side of kqueue(2) and
then support for select(2) and poll(2) will be available “for free”.
Assignment 2 specified that ccid(4) only had to support blocking operation, meaning that when a write or
read operation was performed the kernel was able to wait or sleep in the ccidwrite() or ccidread() handlers
respectively for the underlying USB operation to complete. This assignment requires the implementation
of non-blocking read and write against ccid(4) devices, and support for the kqueue(2) event notification
mechanism.
3 Specifications
You will be modifying the ccid(4) driver written for Assignment 2.
4
You may assume that userland tools which use your driver will directly open the device and use the
read/write/kqfilter/ioctl interfaces specified below. You do not need to implement any additional userland
components to go with it.
3.1 CCID Functionality
The core CCID functionality remains the same as specified in assignment 2, with the following changes:
Multiple slot support: you are now required to implement support for multiple-slot CCID devices, with up
to 8 slots
Timeouts: you are now required to use a fixed timeout for CCID commands (specified in detail below).
You are also required to handle time extension requests.
Abort sequence: you are now required to use the CCID abort sequence (control transfer and bulk transfer,
including sequence number) in certain situations, including command timeouts and unclean terminations of
a transaction with an outstanding command.
The following are still true in A3:
APDU-layer only: you are only required to support readers which advertise APDU-layer operation (meaning
that your driver does not have to implement T=0 or T=1 TPDU layer encapsulation)
Automatic clock, voltage and baud rate only: you are not required to work out what voltage, clock
speed or baud rate is required by a card, and should only support readers that handle this automatically.
PPS support not required: along with being APDU-layer only, and only supporting automatic clock speed
operation, you are not required to implement PPS
No automatic power-on: you must assume the CCID reader requires an explicit power-on CCID message to
power up the ICC (i.e. the Automatic activation of ICC on inserting CCID feature is not available)
You are only required to implement enough of the CCID commands to provide the device interface in this
specification. There are CCID commands (e.g. PC_to_RDR_Mechanical for readers which can physically eject or
lock their card slots) which you do not need to implement any support for, since they have no matching ioctl
or requirement in the device interface.
Part of the objective of this assignment is reading and interpreting the CCID specification, and working out the
exact commands which will be required to implement the required behaviour. You should pay close attention to
the descriptions of each command in the specification, and read the example exchanges towards the end of the
document, to work out which are required.
3.2 Command timeouts
The ccid(4) device driver must use the following time-outs on commands:
Command Timeout
Power on 30 sec
Power off 5 sec
APDU (XfrBlock) 5 sec
APDU time extension +(bError*5) sec
(All others) 5 sec
Each time CCID reader returns a time extension request during an APDU command, wait an additional 5 seconds
multiplied by the value in bError (i.e. assume that BWT or WWT is 5 seconds).
If a command time-out is reached, then ccid(4) must abort that command using the CCID abort sequence.
The errno returned to userland due to a command timeout should be ETIMEDOUT (which does not modify the
result of CCIDIOC_GET_LAST_ERR).
5
Note that timeouts are per-command, not per-transfer.
3.3 Device special file interface
The ccid(4) device special file must continue to use major number 101 on the amd64 architecture for a character
device special file.
The device special configuration glue for ccid(4) should be modified to set D_CLONE. The bits of the device
minor below CLONE_SHIFT identify the ccid(4) hardware device instance (unit) and slot being accessed.
The following structure must be used for the bits of the minor number:
(MSB..LSB) 24..CLONE_SHIFT (CLONE_SHIFT - 1)..3 3..1
(contains) Clone number (16 bits) Unit number (5 bits) Slot number (3 bits)
For example, the minor number 123 corresponds to unit 15, slot 3, clone 0. The 2nd slot on the 3rd CCID
device, clone number 2 would be minor number 538.
The naming scheme for the special files in /dev expected by the ccidctl code provided is /dev/ccidXY, where
X is the unit number as a numeric string, and Y is a for slot 0, b for slot 1, c for slot 2, etc.
Examples:
Device Major Minor (Unit) (Slot)
/dev/ccid0a 101 0 0 0
/dev/ccid0b 101 1 0 1
/dev/ccid0c 101 2 0 2
/dev/ccid1a 101 8 1 0
/dev/ccid1b 101 9 1 1
/dev/ccid2a 101 16 2 0
/dev/ccid2b 101 17 2 1
Note that the on-disk device nodes always use clone number 0 (the kernel will fill in that part of the minor number
automatically).
Your driver may, if you wish, return additional errno(2) values as well as those specified below, in situations
that are not covered by the errno values and descriptions listed. However, it is important to note that the
driver must not return EBADMSG in any situation that is not able to sensibly update the return value of the
CCIDIOC_GET_LAST_ERR ioctl command (see below).
It’s important to note that the errno(2) specifications are very different to A2. For example, the use of
EBADMSG and EIO in A2 is the opposite way around to A3. Be careful not to confuse them.
3.3.1 open() entry point
The open() handler should ensure that the non-clone bits of the minor number have an associated kernel driver
instance with the relevant unit number, and that that device has enough slots for the slot number requested.
Access control is provided by permissions on the filesystem.
The open() handler should return the following errors for the reasons listed. On success it should return 0.
ENXIO no hardware driver is attached with the relevant unit number or the device does not have sufficient slots
6
3.3.2 ioctl()
ccid(4) specific ioctls used by the userland testing tool are provided in src/sys/dev/usb/ccidvar.h. This
header specifies both the ioctl magic numbers (CCIDIOC_* macros) and the structs used as arguments to them.
You must not modify the ioctl definitions provided: they are the interface that is required for testing.
3.3.2.1 CCIDIOC_XACT_BEGIN
This ioctl begins a transaction for the current device clone instance. If another device clone instance already has
a transaction in progress, block until the current device clone instance can begin a transaction.
On beginning the transaction, any existing error state that could be reported by the CCIDIOC_GET_LAST_ERR
ioctl is cleared.
If the smartcard is powered off, or there is a pending power state change (e.g. from the previous transaction, if
the initial attempt to reset or power down failed) then a power-on message is sent to the reader to power it on.
This ioctl() will always block until the power-on command is completed.
EACCES the current device clone instance is open read-only.
EDEADLK the current device clone instance is already within a transaction.
EBADMSG the CCID reader returned an error response to a CCID-level command (e.g. no card/ICC present,
hardware error).
3.3.2.2 CCIDIOC_XACT_TRY_BEGIN
The CCIDIOC_XACT_TRY_BEGIN ioctl attempts to begin a transaction, but returns an error if it is unable to do
so immediately due to another extant transaction on the slot.
If this ioctl successfully begins a transaction, any existing error state that could be reported by the
CCIDIOC_GET_LAST_ERR ioctl is cleared.
If the smartcard is powered off, or there is a pending power state change (e.g. from the previous transaction, if
the initial attempt to reset or power down failed) then a power-on message is sent to the reader to power it on.
This ioctl() does not block waiting for the response to the power-on message – any errors received during
power-on will instead be seen by the first write() call after the transaction begins.
EACCES the current device clone instance is open read-only.
EDEADLK the current device clone instance is already within a transaction.
EAGAIN a different device clone instance (e.g. in another process) is already within a transaction for this device.
3.3.2.3 CCIDIOC_XACT_COMMIT
Signal the end of the transaction. This will then allow another device clone instance to begin a transaction.
To avoid blocking in this ioctl, the program should wait until the file descriptor becomes writable or readable
first. CCIDIOC_XACT_COMMIT after a write() operation (but before the matching read()) starts a CCID abort
sequence that must complete before the transaction ends.
The CCIDIOC_XACT_COMMIT variant of a transaction commit can be used by a program when it did not change
the security disposition of the smartcard.
EPERM the current device clone instance is not currently within a transaction.
3.3.2.4 CCIDIOC_XACT_RESET_COMMIT
Send a power on command to the reader to begin a warm reset of the smart card, and then end the transaction.
This will then allow another device clone instance to begin a transaction after the reset has completed.
7
To avoid blocking in this ioctl, the program should wait until the file descriptor becomes writable or readable
first. CCIDIOC_XACT_RESET_COMMIT after a write() operation (but before the matching read()) starts a CCID
abort sequence that must complete before the transaction ends.
The CCIDIOC_XACT_RESET_COMMIT variant of a transaction commit is used by a program when it changed the
security disposition of the smartcard, eg, if a credential such as a PIN was sent to the card to unlock the use of
certain keys or features.
Errors during the abort sequence (if one is needed) or during the warm reset command or any other CCID
commands made are not returned to this ioctl(). If the relevant commands fail, a message should be written
to the system console and dmesg, and the operations should be retried during the next CCIDIOC_XACT_BEGIN,
with errors reported there (or in the first write() afterwards if non-blocking).
EPERM the device clone instance is not currently within a transaction.
3.3.2.5 CCIDIOC_XACT_POWEROFF_COMMIT
Send a power off command to the reader to begin a cold reset of the smartcard, and then end the transaction.
This will then allow another device clone instance to begin a transaction with a cold-reset device.
To avoid blocking in this ioctl, the program should wait until the file descriptor becomes writable or readable first.
CCIDIOC_XACT_POWEROFF_COMMIT after a write() operation (but before the matching read()) starts a CCID
abort sequence that must complete before the transaction ends.
The CCIDIOC_XACT_POWEROFF_COMMIT variant of a transaction commit is used by a program when it changed
the security disposition of the smart card, eg, if a credential such as a PIN was sent to the card to unlock the
use of certain keys or features.
Errors during the abort sequence (if one is needed) or during the power-off command or any other CCID
commands made are not returned to this ioctl(). If the relevant commands fail, they should be retried during
the next CCIDIOC_XACT_BEGIN, and errors reported there (or in the first write() afterwards if non-blocking).
EPERM the current device clone instance is not currently within a transaction.
3.3.2.6 CCIDIOC_GET_LAST_ERR
The CCIDIOC_GET_LAST_ERR ioctl should provide more detailed information about an error which resulted in
EBADMSG being returned by read() or another ioctl (such as CCIDIOC_XACT_BEGIN).
It contains both a numeric error code and a string description which the driver should fill out based on the error
description tables in the CCID specification. The string should be of the format ERROR NAME: Cause string.
For example, the numeric error code -2 should return a string similar to ICC_MUTE: No ICC present, or CCID
timed out while talking to the ICC.
Our tests will look for the “ERROR NAME” at the start of the string to match the CCID specification, but will
not require any particular cause description following it.
The “last error” state is stored per device clone instance. If no error has occurred since this clone instance was
opened, the ioctl should return ENOMSG instead of a ccid_err_info struct.
This ioctl may fail with errors for the following reasons:
EPERM the current device clone instance is not currently within a transaction.
ENOMSG no error has occurred within the current transaction.
8
3.3.2.7 CCIDIOC_GET_ATR
This ioctl retrieves the last answer-to-reset (ATR) string returned by a card in the CCID reader. If the CCID
device has never been powered up, or if the last message from the CCID device indicated that no card is present,
this should return ENOMSG.
It can be run at any time, regardless of transaction status.
The ATR is returned inside a struct ccid_atr. The len field is set to the length of the ATR, and the actual
bytes of the ATR are written into the buf field.
This ioctl may fail with errors for the following reasons:
ENOMSG the card in this slot has never been powered up, or the last message from the reader indicated that
no card is present.
3.3.2.8 CCIDIOC_GET_DRIVER_INFO
This ioctl must be implemented as specified in Assignment 2, with one variation: the ccid_driver_slot member
must be filled out with the slot index which is open. The A3 base patch contains a working implementation
(which sets the slot index to 0 always).
3.3.2.9 CCIDIOC_GET_DESCRIPTOR
This ioctl must be implemented as specified in Assignment 2. The A3 base patch contains a complete
implementation.
3.3.2.10 USB_DEVICEINFO
This ioctl must be implemented as specified in Assignment 2. The A3 base patch contains a complete
implementation.
3.3.3 write() entry point
The write handler allows userland to send an APDU to a smartcard that is inserted into the reader.
An entire APDU must be provided to write() in a single call – one APDU cannot be spread across multiple
write() calls. A call to writev() is treated as a single write (even if it uses multiple iovecs).
The write() handler encapsulates the APDU bytes from userland in a CCID message and submits it to the
bulk-out message USB endpoint.
A write() must be performed within a ccid(4) transaction begun by the current device clone instance.
A blocking write must wait until at least the command submission to the CCID reader has been completed.
A non-blocking write must begin the command submission to the CCID reader and return.
Note: if transactions are not implemented, then write() on a device must implicitly power up the card, if it is
not presently powered up.
EAGAIN non-blocking I/O was requested, but the operation would block.
EPERM the current device clone instance has not begun a transaction.
EBUSY a request is already in progress – either waiting for a response from the reader or a response has been
received but read() has not yet consumed it.
EACCES the device is open read-only.
EBADMSG a preceding non-blocking read or poweron command had a CCID error, or a CCID error occurred
during blocking operation which prevented sending the command.
9
EIO a preceding non-blocking read had a USB error, or a USB level error occurred during blocking operation
that prevented sending the command.
3.3.4 read() entry point
read() gets a response APDU from the smartcard inserted into the relevant slot of the CCID reader via the
bulk-in response USB endpoint.
The response matches the previous write() made to this device clone instance.
A blocking read() will wait for a response CCID message from the reader related to the most recent write()
call that submitted a request. It should block until the response is available.
The response APDU (or the portion of it that will fit) will be transferred to the buffer supplied by userland. No
CCID-level headers or structures are included (just the response APDU itself).
EAGAIN non-blocking I/O was requested, but the operation would block.
EPERM the current device clone instance has not begun a transaction.
EACCES the current device clone instance is open read-only.
EBADMSG the CCID reader returned an error response to the CCID-level command (e.g. no card/ICC present,
hardware error). Note that this does not include errors returned as an APDU-level error by the card (e.g. a
SW other than 0x9000), which are processed as normal response APDUs.
ENOMSG no write request has been made.
EBADMSG a preceding non-blocking write had a CCID-level error, or a CCID-level error occurred during blocking
operation which prevented receiving the response.
EIO a preceding non-blocking write had a USB error, or a USB level error occurred during blocking operation
that prevented receiving the response.
3.3.5 close()
If close() is called while the current device clone instance is in a transaction, any command in progress must be
aborted using the CCID abort sequence, and close() will then implicitly reset the smartcard (a warm reset, by
sending an extra power-on command) and end the transaction.
If no transaction is open, close() does not perform any CCID-level operations.
close() must not return an error. If any operations during close() fail, or the device returns an error, then a
message should be written to the system console and dmesg, and the relevant operations should be retried during