1a61ed2ceSHans Rosenfeld /*
2a61ed2ceSHans Rosenfeld  * This file and its contents are supplied under the terms of the
3a61ed2ceSHans Rosenfeld  * Common Development and Distribution License ("CDDL"), version 1.0.
4a61ed2ceSHans Rosenfeld  * You may only use this file in accordance with the terms of version
5a61ed2ceSHans Rosenfeld  * 1.0 of the CDDL.
6a61ed2ceSHans Rosenfeld  *
7a61ed2ceSHans Rosenfeld  * A full copy of the text of the CDDL should have accompanied this
8a61ed2ceSHans Rosenfeld  * source.  A copy of the CDDL is also available via the Internet at
9a61ed2ceSHans Rosenfeld  * http://www.illumos.org/license/CDDL.
10a61ed2ceSHans Rosenfeld  */
11a61ed2ceSHans Rosenfeld 
12a61ed2ceSHans Rosenfeld /*
13a61ed2ceSHans Rosenfeld  * Copyright 2019, Joyent, Inc.
14a61ed2ceSHans Rosenfeld  */
15a61ed2ceSHans Rosenfeld 
16a61ed2ceSHans Rosenfeld /*
17a61ed2ceSHans Rosenfeld  * USB CCID class driver
18a61ed2ceSHans Rosenfeld  *
19a61ed2ceSHans Rosenfeld  * Slot Detection
20a61ed2ceSHans Rosenfeld  * --------------
21a61ed2ceSHans Rosenfeld  *
22a61ed2ceSHans Rosenfeld  * A CCID reader has one or more slots, each of which may or may not have a ICC
23a61ed2ceSHans Rosenfeld  * (integrated circuit card) present. Some readers actually have a card that's
24a61ed2ceSHans Rosenfeld  * permanently plugged in while other readers allow for cards to be inserted and
25a61ed2ceSHans Rosenfeld  * removed. We model all CCID readers that don't have removable cards as ones
26a61ed2ceSHans Rosenfeld  * that are removable, but never fire any events. Readers with removable cards
27a61ed2ceSHans Rosenfeld  * are required to have an Interrupt-IN pipe.
28a61ed2ceSHans Rosenfeld  *
29a61ed2ceSHans Rosenfeld  * Each slot starts in an unknown state. After attaching we always kick off a
30a61ed2ceSHans Rosenfeld  * discovery. When a change event comes in, that causes us to kick off a
31a61ed2ceSHans Rosenfeld  * discovery again, though we focus it only on those endpoints that have noted a
32a61ed2ceSHans Rosenfeld  * change. At attach time we logically mark that every endpoint has changed,
33a61ed2ceSHans Rosenfeld  * allowing us to figure out what its actual state is. We don't rely on any
34a61ed2ceSHans Rosenfeld  * initial Interrupt-IN polling to allow for the case where either the hardware
35a61ed2ceSHans Rosenfeld  * doesn't report it or to better handle the devices without an Interrupt-IN
36a61ed2ceSHans Rosenfeld  * entry. Just because we open up the Interrupt-IN pipe, hardware is not
37a61ed2ceSHans Rosenfeld  * obligated to tell us, as the detaching and reattaching of a driver will not
38a61ed2ceSHans Rosenfeld  * cause a power cycle.
39a61ed2ceSHans Rosenfeld  *
40a61ed2ceSHans Rosenfeld  * The Interrupt-IN exception callback may need to restart polling. In addition,
41a61ed2ceSHans Rosenfeld  * we may fail to start or restart polling due to a transient issue. In cases
42a61ed2ceSHans Rosenfeld  * where the attempt to start polling has failed, we try again in one second
43a61ed2ceSHans Rosenfeld  * with a timeout.
44a61ed2ceSHans Rosenfeld  *
45a61ed2ceSHans Rosenfeld  * Discovery is run through a taskq. The various slots are checked serially. If
46a61ed2ceSHans Rosenfeld  * a discovery is running when another change event comes in, we flag ourselves
47a61ed2ceSHans Rosenfeld  * for a follow up run. This means that it's possible that we end up processing
48a61ed2ceSHans Rosenfeld  * items early and that the follow up run is ignored.
49a61ed2ceSHans Rosenfeld  *
50a61ed2ceSHans Rosenfeld  * Two state flags are used to keep track of this dance: CCID_F_WORKER_REQUESTED
51a61ed2ceSHans Rosenfeld  * and CCID_F_WORKER_RUNNING. The first is used to indicate that discovery is
52a61ed2ceSHans Rosenfeld  * desired. The second is to indicate that it is actively running. When
53a61ed2ceSHans Rosenfeld  * discovery is requested, the caller first checks the current flags. If neither
54a61ed2ceSHans Rosenfeld  * flag is set, then it knows that it can kick off discovery. Regardless if it
55a61ed2ceSHans Rosenfeld  * can kick off the taskq, it always sets requested. Once the taskq entry
56a61ed2ceSHans Rosenfeld  * starts, it removes any DISCOVER_REQUESTED flags and sets DISCOVER_RUNNING. If
57a61ed2ceSHans Rosenfeld  * at the end of discovery, we find that another request has been made, the
58a61ed2ceSHans Rosenfeld  * discovery function will kick off another entry in the taskq.
59a61ed2ceSHans Rosenfeld  *
60a61ed2ceSHans Rosenfeld  * The one possible problem with this model is that it means that we aren't
61a61ed2ceSHans Rosenfeld  * throttling the set of incoming requests with respect to taskq dispatches.
62a61ed2ceSHans Rosenfeld  * However, because these are only driven by an Interrupt-IN pipe, it is hoped
63a61ed2ceSHans Rosenfeld  * that the frequency will be rather reduced. If it turns out that that's not
64a61ed2ceSHans Rosenfeld  * the case, we may need to use a timeout or another trick to ensure that only
65a61ed2ceSHans Rosenfeld  * one discovery per tick or so is initialized. The main reason we don't just do
66a61ed2ceSHans Rosenfeld  * that off the bat and add a delay is because of contactless cards which may
67a61ed2ceSHans Rosenfeld  * need to be acted upon in a soft real-time fashion.
68a61ed2ceSHans Rosenfeld  *
69a61ed2ceSHans Rosenfeld  * Command Handling
70a61ed2ceSHans Rosenfeld  * ----------------
71a61ed2ceSHans Rosenfeld  *
72a61ed2ceSHans Rosenfeld  * Commands are issued to a CCID reader on a Bulk-OUT pipe. Responses are
73a61ed2ceSHans Rosenfeld  * generated as a series of one or more messages on a Bulk-IN pipe. To correlate
74a61ed2ceSHans Rosenfeld  * these commands a sequence number is used. This sequence number is one byte
75a61ed2ceSHans Rosenfeld  * and can be in the range [ CCID_SEQ_MIN, CCID_SEQ_MAX ]. To keep track of the
76a61ed2ceSHans Rosenfeld  * allocated IDs we leverage an ID space.
77a61ed2ceSHans Rosenfeld  *
78a61ed2ceSHans Rosenfeld  * A CCID reader contains a number of slots. Each slot can be addressed
79a61ed2ceSHans Rosenfeld  * separately as each slot represents a separate place that a card may be
80a61ed2ceSHans Rosenfeld  * inserted or not. A given slot may only have a single outstanding command. A
81a61ed2ceSHans Rosenfeld  * given CCID reader may only have a number of commands outstanding to the CCID
82a61ed2ceSHans Rosenfeld  * device as a whole based on a value in the class descriptor (see the
83a61ed2ceSHans Rosenfeld  * ccd_bMacCCIDBusySlots member of the ccid_class_descr_t).
84a61ed2ceSHans Rosenfeld  *
85a61ed2ceSHans Rosenfeld  * To simplify the driver, we only support issuing a single command to a CCID
86a61ed2ceSHans Rosenfeld  * reader at any given time. All commands that are outstanding are queued in a
87a61ed2ceSHans Rosenfeld  * per-device list ccid_command_queue. The head of the queue is the current
88a61ed2ceSHans Rosenfeld  * command that we believe is outstanding to the reader or will be shortly. The
89a61ed2ceSHans Rosenfeld  * command is issued by sending a Bulk-OUT request with a CCID header. Once we
90a61ed2ceSHans Rosenfeld  * have the Bulk-OUT request acknowledged, we begin sending Bulk-IN messages to
91a61ed2ceSHans Rosenfeld  * the controller. Once the Bulk-IN message is acknowledged, then we complete
92a61ed2ceSHans Rosenfeld  * the command and proceed to the next command. This is summarized in the
93a61ed2ceSHans Rosenfeld  * following state machine:
94a61ed2ceSHans Rosenfeld  *
95a61ed2ceSHans Rosenfeld  *              +-----------------------------------------------------+
96a61ed2ceSHans Rosenfeld  *              |                                                     |
97a61ed2ceSHans Rosenfeld  *              |                        ccid_command_queue           |
98a61ed2ceSHans Rosenfeld  *              |                    +---+---+---------+---+---+      |
99a61ed2ceSHans Rosenfeld  *              v                    | h |   |         |   | t |      |
100a61ed2ceSHans Rosenfeld  *  +-------------------------+      | e |   |         |   | a |      |
101a61ed2ceSHans Rosenfeld  *  | ccid_command_dispatch() |<-----| a |   |   ...   |   | i |      |
102a61ed2ceSHans Rosenfeld  *  +-----------+-------------+      | d |   |         |   | l |      |
103a61ed2ceSHans Rosenfeld  *              |                    +---+---+---------+---+---+      |
104a61ed2ceSHans Rosenfeld  *              v                                                     |
105a61ed2ceSHans Rosenfeld  *  +-------------------------+      +-------------------------+      |
106a61ed2ceSHans Rosenfeld  *  | usb_pipe_bulk_xfer()    |----->| ccid_dispatch_bulk_cb() |      |
107a61ed2ceSHans Rosenfeld  *  | ccid_bulkout_pipe       |      +------------+------------+      |
108a61ed2ceSHans Rosenfeld  *  +-------------------------+                   |                   |
109a61ed2ceSHans Rosenfeld  *                                                |                   |
110a61ed2ceSHans Rosenfeld  *              |                                 v                   |
111a61ed2ceSHans Rosenfeld  *              |                    +-------------------------+      |
112a61ed2ceSHans Rosenfeld  *              |                    | ccid_bulkin_schedule()  |      |
113a61ed2ceSHans Rosenfeld  *              v                    +------------+------------+      |
114a61ed2ceSHans Rosenfeld  *                                                |                   |
115a61ed2ceSHans Rosenfeld  *     /--------------------\                     |                   |
116a61ed2ceSHans Rosenfeld  *    /                      \                    v                   |
117a61ed2ceSHans Rosenfeld  *    |  ###    CCID HW      |       +-------------------------+      |
118a61ed2ceSHans Rosenfeld  *    |  ###                 |       | usb_pipe_bulk_xfer()    |      |
119a61ed2ceSHans Rosenfeld  *    |                      | ----> | ccid_bulkin_pipe        |      |
120a61ed2ceSHans Rosenfeld  *    |                      |       +------------+------------+      |
121a61ed2ceSHans Rosenfeld  *    \                      /                    |                   |
122a61ed2ceSHans Rosenfeld  *     \--------------------/                     |                   |
123a61ed2ceSHans Rosenfeld  *                                                v                   |
124a61ed2ceSHans Rosenfeld  *                                   +-------------------------+      |
125a61ed2ceSHans Rosenfeld  *                                   | ccid_reply_bulk_cb()    |      |
126a61ed2ceSHans Rosenfeld  *                                   +------------+------------+      |
127a61ed2ceSHans Rosenfeld  *                                                |                   |
128a61ed2ceSHans Rosenfeld  *                                                |                   |
129a61ed2ceSHans Rosenfeld  *                                                v                   |
130a61ed2ceSHans Rosenfeld  *                                   +-------------------------+      |
131a61ed2ceSHans Rosenfeld  *                                   | ccid_command_complete() +------+
132a61ed2ceSHans Rosenfeld  *                                   +-------------------------+
133a61ed2ceSHans Rosenfeld  *
134a61ed2ceSHans Rosenfeld  *
135a61ed2ceSHans Rosenfeld  * APDU and TPDU Processing and Parameter Selection
136a61ed2ceSHans Rosenfeld  * ------------------------------------------------
137a61ed2ceSHans Rosenfeld  *
138a61ed2ceSHans Rosenfeld  * Readers provide four different modes for us to be able to transmit data to
139a61ed2ceSHans Rosenfeld  * and from the card. These are:
140a61ed2ceSHans Rosenfeld  *
141a61ed2ceSHans Rosenfeld  *   1. Character Mode
142a61ed2ceSHans Rosenfeld  *   2. TPDU Mode
143a61ed2ceSHans Rosenfeld  *   3. Short APDU Mode
144a61ed2ceSHans Rosenfeld  *   4. Extended APDU Mode
145a61ed2ceSHans Rosenfeld  *
146a61ed2ceSHans Rosenfeld  * Readers either support mode 1, mode 2, mode 3, or mode 3 and 4. All readers
147a61ed2ceSHans Rosenfeld  * that support extended APDUs support short APDUs. At this time, we do not
148a61ed2ceSHans Rosenfeld  * support character mode or TPDU mode, and we use only short APDUs even for
149a61ed2ceSHans Rosenfeld  * readers that support extended APDUs.
150a61ed2ceSHans Rosenfeld  *
151a61ed2ceSHans Rosenfeld  * The ICC and the reader need to be in agreement in order for them to be able
152a61ed2ceSHans Rosenfeld  * to exchange information. The ICC indicates what it supports by replying to a
153a61ed2ceSHans Rosenfeld  * power on command with an ATR (answer to reset). This data can be parsed to
154a61ed2ceSHans Rosenfeld  * indicate which of two protocols the ICC supports. These protocols are
155a61ed2ceSHans Rosenfeld  * referred to as:
156a61ed2ceSHans Rosenfeld  *
157a61ed2ceSHans Rosenfeld  *  o T=0
158a61ed2ceSHans Rosenfeld  *  o T=1
159a61ed2ceSHans Rosenfeld  *
160a61ed2ceSHans Rosenfeld  * These protocols are defined in the ISO/IEC 7816-3:2006 specification. When a
161a61ed2ceSHans Rosenfeld  * reader supports an APDU mode, then the driver does not have to worry about
162a61ed2ceSHans Rosenfeld  * the underlying protocol and can just send an application data unit (APDU).
163a61ed2ceSHans Rosenfeld  * Otherwise, the driver must take the application data (APDU) and transform it
164a61ed2ceSHans Rosenfeld  * into the form required by the corresponding protocol.
165a61ed2ceSHans Rosenfeld  *
166a61ed2ceSHans Rosenfeld  * There are several parameters that need to be negotiated to ensure that the
167a61ed2ceSHans Rosenfeld  * protocols work correctly. To negotiate these parameters and to select a
168a61ed2ceSHans Rosenfeld  * protocol, the driver must construct a PPS (protocol and parameters structure)
169a61ed2ceSHans Rosenfeld  * request and exchange that with the ICC. A reader may optionally take care of
170a61ed2ceSHans Rosenfeld  * performing this and indicates its support for this in dwFeatures member of
171a61ed2ceSHans Rosenfeld  * the USB class descriptor.
172a61ed2ceSHans Rosenfeld  *
173a61ed2ceSHans Rosenfeld  * In addition, the reader itself must often be told of these configuration
174a61ed2ceSHans Rosenfeld  * changes through the means of a CCID_REQUEST_SET_PARAMS command. Once both of
175a61ed2ceSHans Rosenfeld  * these have been performed, the reader and ICC can communicate to their hearts
176a61ed2ceSHans Rosenfeld  * desire.
177a61ed2ceSHans Rosenfeld  *
178a61ed2ceSHans Rosenfeld  * Both the negotiation and the setting of the parameters can be performed
179a61ed2ceSHans Rosenfeld  * automatically by the CCID reader. When the reader supports APDU exchanges,
180a61ed2ceSHans Rosenfeld  * then it must support some aspects of this negotiation. Because of that, we
181a61ed2ceSHans Rosenfeld  * never consider performing this and only support readers that offer this kind
182a61ed2ceSHans Rosenfeld  * of automation.
183a61ed2ceSHans Rosenfeld  *
184a61ed2ceSHans Rosenfeld  * User I/O Basics
185a61ed2ceSHans Rosenfeld  * ---------------
186a61ed2ceSHans Rosenfeld  *
187a61ed2ceSHans Rosenfeld  * A user performs I/O by writing APDUs (Application Protocol Data Units). A
188a61ed2ceSHans Rosenfeld  * user issues a system call that ends up in write(9E) (write(2), writev(2),
189a61ed2ceSHans Rosenfeld  * pwrite(2), pwritev(2), etc.). The user data is consumed by the CCID driver
190a61ed2ceSHans Rosenfeld  * and a series of commands will then be issued to the device, depending on the
191a61ed2ceSHans Rosenfeld  * protocol mode. The write(9E) call does not block for this to finish. Once
192a61ed2ceSHans Rosenfeld  * write(9E) has returned, the user may block in a read(2) related system call
193a61ed2ceSHans Rosenfeld  * or poll for POLLIN.
194a61ed2ceSHans Rosenfeld  *
195a61ed2ceSHans Rosenfeld  * A thread may not call read(9E) without having called write(9E). This model is
196a61ed2ceSHans Rosenfeld  * due to the limited capability of hardware. Only a single command can be going
197a61ed2ceSHans Rosenfeld  * on a given slot and due to the fact that many commands change the hardware
198a61ed2ceSHans Rosenfeld  * state, we do not try to multiplex multiple calls to write() or read().
199a61ed2ceSHans Rosenfeld  *
200a61ed2ceSHans Rosenfeld  *
201a61ed2ceSHans Rosenfeld  * User I/O, Transaction Ends, ICC removals, and Reader Removals
202a61ed2ceSHans Rosenfeld  * -------------------------------------------------------------
203a61ed2ceSHans Rosenfeld  *
204a61ed2ceSHans Rosenfeld  * While the I/O model given to user land is somewhat simple, there are a lot of
205a61ed2ceSHans Rosenfeld  * tricky pieces to get right because we are in a multi-threaded pre-emptible
206a61ed2ceSHans Rosenfeld  * system. In general, there are four different levels of state that we need to
207a61ed2ceSHans Rosenfeld  * keep track of:
208a61ed2ceSHans Rosenfeld  *
209a61ed2ceSHans Rosenfeld  *   1. User threads in I/O
210a61ed2ceSHans Rosenfeld  *   2. Kernel protocol level support (T=1, apdu, etc.).
211a61ed2ceSHans Rosenfeld  *   3. Slot/ICC state
212a61ed2ceSHans Rosenfeld  *   4. CCID Reader state
213a61ed2ceSHans Rosenfeld  *
214a61ed2ceSHans Rosenfeld  * Of course, each level cares about the state above it. The kernel protocol
215a61ed2ceSHans Rosenfeld  * level state (2) cares about the User threads in I/O (1). The same is true
216a61ed2ceSHans Rosenfeld  * with the other levels caring about the levels above it. With this in mind
217a61ed2ceSHans Rosenfeld  * there are three non-data path things that can go wrong:
218a61ed2ceSHans Rosenfeld  *
219a61ed2ceSHans Rosenfeld  *   A. The user can end a transaction (whether through an ioctl or close(9E)).
220a61ed2ceSHans Rosenfeld  *   B. The ICC can be removed
221a61ed2ceSHans Rosenfeld  *   C. The CCID device can be removed or reset at a USB level.
222a61ed2ceSHans Rosenfeld  *
223a61ed2ceSHans Rosenfeld  * Each of these has implications on the outstanding I/O and other states of
224a61ed2ceSHans Rosenfeld  * the world. When events of type A occur, we need to clean up states 1 and 2.
225a61ed2ceSHans Rosenfeld  * Then events of type B occur we need to clean up states 1-3. When events of
226a61ed2ceSHans Rosenfeld  * type C occur we need to clean up states 1-4. The following discusses how we
227a61ed2ceSHans Rosenfeld  * should clean up these different states:
228a61ed2ceSHans Rosenfeld  *
229a61ed2ceSHans Rosenfeld  * Cleaning up State 1:
230a61ed2ceSHans Rosenfeld  *
231a61ed2ceSHans Rosenfeld  *   To clean up the User threads in I/O there are three different cases to
232a61ed2ceSHans Rosenfeld  *   consider. The first is cleaning up a thread that is in the middle of
233a61ed2ceSHans Rosenfeld  *   write(9E). The second is cleaning up thread that is blocked in read(9E).
234a61ed2ceSHans Rosenfeld  *   The third is dealing with threads that are stuck in chpoll(9E).
235a61ed2ceSHans Rosenfeld  *
236a61ed2ceSHans Rosenfeld  *   To handle the write case, we have a series of flags that is on the CCID
237a61ed2ceSHans Rosenfeld  *   slot's I/O structure (ccid_io_t, cs_io on the ccid_slot_t). When a thread
238a61ed2ceSHans Rosenfeld  *   begins its I/O it will set the CCID_IO_F_PREPARING flag. This flag is used
239a61ed2ceSHans Rosenfeld  *   to indicate that there is a thread that is performing a write(9E), but it
240a61ed2ceSHans Rosenfeld  *   is not holding the ccid_mutex because of the operations that it is taking.
241a61ed2ceSHans Rosenfeld  *   Once it has finished, the thread will remove that flag and instead
242a61ed2ceSHans Rosenfeld  *   CCID_IO_F_IN_PROGRESS will be set. If we find that the CCID_IO_F_PREPARING
243a61ed2ceSHans Rosenfeld  *   flag is set, then we will need to wait for it to be removed before
244a61ed2ceSHans Rosenfeld  *   continuing. The fact that there is an outstanding physical I/O will be
245a61ed2ceSHans Rosenfeld  *   dealt with when we clean up state 2.
246a61ed2ceSHans Rosenfeld  *
247a61ed2ceSHans Rosenfeld  *   To handle the read case, we have a flag on the ccid_minor_t which indicates
248a61ed2ceSHans Rosenfeld  *   that a thread is blocked on a condition variable (cm_read_cv), waiting for
249a61ed2ceSHans Rosenfeld  *   the I/O to complete. The way this gets cleaned up varies a bit on each of
250a61ed2ceSHans Rosenfeld  *   the different cases as each one will trigger a different error to the
251a61ed2ceSHans Rosenfeld  *   thread. In all cases, the condition variable will be signaled. Then,
252a61ed2ceSHans Rosenfeld  *   whenever the thread comes out of the condition variable it will always
253a61ed2ceSHans Rosenfeld  *   check the state to see if it has been woken up because the transaction is
254a61ed2ceSHans Rosenfeld  *   being closed, the ICC has been removed, or the reader is being
255a61ed2ceSHans Rosenfeld  *   disconnected. In all such cases, the thread in read will end up receiving
256a61ed2ceSHans Rosenfeld  *   an error (ECANCELED, ENXIO, and ENODEV respectively).
257a61ed2ceSHans Rosenfeld  *
258a61ed2ceSHans Rosenfeld  *   If we have hit the case that this needs to be cleaned up, then the
259a61ed2ceSHans Rosenfeld  *   CCID_MINOR_F_READ_WAITING flag will be set on the ccid_minor_t's flags
260a61ed2ceSHans Rosenfeld  *   member (cm_flags). In this case, the broader system must change the
261a61ed2ceSHans Rosenfeld  *   corresponding system state flag for the appropriate condition, signal the
262a61ed2ceSHans Rosenfeld  *   read cv, and then wait on an additional cv in the minor, the
263a61ed2ceSHans Rosenfeld  *   ccid_iowait_cv).
264a61ed2ceSHans Rosenfeld  *
265a61ed2ceSHans Rosenfeld  *   Cleaning up the poll state is somewhat simpler. If any of the conditions
266a61ed2ceSHans Rosenfeld  *   (A-C) occur, then we must flag POLLERR. In addition if B and C occur, then
267a61ed2ceSHans Rosenfeld  *   we will flag POLLHUP at the same time. This will guarantee that any threads
268a61ed2ceSHans Rosenfeld  *   in poll(9E) are woken up.
269a61ed2ceSHans Rosenfeld  *
270a61ed2ceSHans Rosenfeld  * Cleaning up State 2.
271a61ed2ceSHans Rosenfeld  *
272a61ed2ceSHans Rosenfeld  *   While the user I/O thread is a somewhat straightforward, the kernel
273a61ed2ceSHans Rosenfeld  *   protocol level is a bit more complicated. The core problem is that when a
274a61ed2ceSHans Rosenfeld  *   user issues a logical I/O through an APDU, that may result in a series of
275a61ed2ceSHans Rosenfeld  *   one or more protocol level physical commands. The core crux of the issue
276a61ed2ceSHans Rosenfeld  *   with cleaning up this state is twofold:
277a61ed2ceSHans Rosenfeld  *
278a61ed2ceSHans Rosenfeld  *     1. We don't want to block a user thread while I/O is outstanding
279a61ed2ceSHans Rosenfeld  *     2. We need to take one of several steps to clean up the aforementioned
280a61ed2ceSHans Rosenfeld  *        I/O
281a61ed2ceSHans Rosenfeld  *
282a61ed2ceSHans Rosenfeld  *   To try and deal with that, there are a number of different things that we
283a61ed2ceSHans Rosenfeld  *   do. The first thing we do is that we clean up the user state based on the
284a61ed2ceSHans Rosenfeld  *   notes in cleaning up in State 1. Importantly we need to _block_ on this
285a61ed2ceSHans Rosenfeld  *   activity.
286a61ed2ceSHans Rosenfeld  *
287a61ed2ceSHans Rosenfeld  *   Once that is done, we need to proceed to step 2. Since we're doing only
288a61ed2ceSHans Rosenfeld  *   APDU processing, this is as simple as waiting for that command to complete
289a61ed2ceSHans Rosenfeld  *   and/or potentially issue an abort or reset.
290a61ed2ceSHans Rosenfeld  *
291a61ed2ceSHans Rosenfeld  *   While this is ongoing an additional flag (CCID_SLOT_F_NEED_IO_TEARDOWN)
292a61ed2ceSHans Rosenfeld  *   will be set on the slot to make sure that we know that we can't issue new
293a61ed2ceSHans Rosenfeld  *   I/O or that we can't proceed to the next transaction until this phase is
294a61ed2ceSHans Rosenfeld  *   finished.
295a61ed2ceSHans Rosenfeld  *
296a61ed2ceSHans Rosenfeld  * Cleaning up State 3
297a61ed2ceSHans Rosenfeld  *
298a61ed2ceSHans Rosenfeld  *   When the ICC is removed, this is not dissimilar to the previous states. To
299a61ed2ceSHans Rosenfeld  *   handle this we need to first make sure that state 1 and state 2 are
300a61ed2ceSHans Rosenfeld  *   finished being cleaned up. We will have to _block_ on this from the worker
301a61ed2ceSHans Rosenfeld  *   thread. The problem is that we have certain values such as the operations
302a61ed2ceSHans Rosenfeld  *   vector, the ATR data, etc. that we need to make sure are still valid while
303a61ed2ceSHans Rosenfeld  *   we're in the process of cleaning up state. Once all that is done and the
304a61ed2ceSHans Rosenfeld  *   worker thread proceeds we will consider processing a new ICC insertion.
305a61ed2ceSHans Rosenfeld  *   The one good side is that if the ICC was removed, then it should be simpler
306a61ed2ceSHans Rosenfeld  *   to handle all of the outstanding I/O.
307a61ed2ceSHans Rosenfeld  *
308a61ed2ceSHans Rosenfeld  * Cleaning up State 4
309a61ed2ceSHans Rosenfeld  *
310a61ed2ceSHans Rosenfeld  *   When the reader is removed, then we need to clean up all the prior states.
311a61ed2ceSHans Rosenfeld  *   However, this is somewhat simpler than the other cases, as once this
312a61ed2ceSHans Rosenfeld  *   happens our detach endpoint will be called to clean up all of our
313a61ed2ceSHans Rosenfeld  *   resources. Therefore, before we call detach, we need to explicitly clean up
314a61ed2ceSHans Rosenfeld  *   state 1; however, we then at this time leave all the remaining state to be
315a61ed2ceSHans Rosenfeld  *   cleaned up during detach(9E) as part of normal tear down.
316a61ed2ceSHans Rosenfeld  */
317a61ed2ceSHans Rosenfeld 
318a61ed2ceSHans Rosenfeld #include <sys/modctl.h>
319a61ed2ceSHans Rosenfeld #include <sys/errno.h>
320a61ed2ceSHans Rosenfeld #include <sys/conf.h>
321a61ed2ceSHans Rosenfeld #include <sys/ddi.h>
322a61ed2ceSHans Rosenfeld #include <sys/sunddi.h>
323a61ed2ceSHans Rosenfeld #include <sys/cmn_err.h>
324a61ed2ceSHans Rosenfeld #include <sys/sysmacros.h>
325a61ed2ceSHans Rosenfeld #include <sys/stream.h>
326a61ed2ceSHans Rosenfeld #include <sys/strsun.h>
327a61ed2ceSHans Rosenfeld #include <sys/strsubr.h>
328a61ed2ceSHans Rosenfeld #include <sys/filio.h>
329a61ed2ceSHans Rosenfeld 
330a61ed2ceSHans Rosenfeld #define	USBDRV_MAJOR_VER	2
331a61ed2ceSHans Rosenfeld #define	USBDRV_MINOR_VER	0
332a61ed2ceSHans Rosenfeld #include <sys/usb/usba.h>
333a61ed2ceSHans Rosenfeld #include <sys/usb/usba/usbai_private.h>
334a61ed2ceSHans Rosenfeld #include <sys/usb/clients/ccid/ccid.h>
335a61ed2ceSHans Rosenfeld #include <sys/usb/clients/ccid/uccid.h>
336a61ed2ceSHans Rosenfeld 
337a61ed2ceSHans Rosenfeld #include <atr.h>
338a61ed2ceSHans Rosenfeld 
339a61ed2ceSHans Rosenfeld /*
340a61ed2ceSHans Rosenfeld  * Set the amount of parallelism we'll want to have from kernel threads which
341a61ed2ceSHans Rosenfeld  * are processing CCID requests. This is used to size the number of asynchronous
342a61ed2ceSHans Rosenfeld  * requests in the pipe policy. A single command can only ever be outstanding to
343a61ed2ceSHans Rosenfeld  * a single slot. However, multiple slots may potentially be able to be
344a61ed2ceSHans Rosenfeld  * scheduled in parallel. However, we don't actually support this at all and
345a61ed2ceSHans Rosenfeld  * we'll only ever issue a single command. This basically covers the ability to
346a61ed2ceSHans Rosenfeld  * have some other asynchronous operation outstanding if needed.
347a61ed2ceSHans Rosenfeld  */
348a61ed2ceSHans Rosenfeld #define	CCID_NUM_ASYNC_REQS	2
349a61ed2ceSHans Rosenfeld 
350a61ed2ceSHans Rosenfeld /*
351a61ed2ceSHans Rosenfeld  * This is the number of Bulk-IN requests that we will have cached per CCID
352a61ed2ceSHans Rosenfeld  * device. While many commands will generate a single response, the commands
353a61ed2ceSHans Rosenfeld  * also have the ability to generate time extensions, which means that we'll
354a61ed2ceSHans Rosenfeld  * want to be able to schedule another Bulk-IN request immediately. If we run
355a61ed2ceSHans Rosenfeld  * out, we will attempt to refill said cache and will not fail commands
356a61ed2ceSHans Rosenfeld  * needlessly.
357a61ed2ceSHans Rosenfeld  */
358a61ed2ceSHans Rosenfeld #define	CCID_BULK_NALLOCED		16
359a61ed2ceSHans Rosenfeld 
360a61ed2ceSHans Rosenfeld /*
361a61ed2ceSHans Rosenfeld  * This is a time in seconds for the bulk-out command to run and be submitted.
362a61ed2ceSHans Rosenfeld  */
363a61ed2ceSHans Rosenfeld #define	CCID_BULK_OUT_TIMEOUT	5
364a61ed2ceSHans Rosenfeld #define	CCID_BULK_IN_TIMEOUT	5
365a61ed2ceSHans Rosenfeld 
366a61ed2ceSHans Rosenfeld /*
367a61ed2ceSHans Rosenfeld  * There are two different Interrupt-IN packets that we might receive. The
368a61ed2ceSHans Rosenfeld  * first, RDR_to_PC_HardwareError, is a fixed four byte packet. However, the
369a61ed2ceSHans Rosenfeld  * other one, RDR_to_PC_NotifySlotChange, varies in size as it has two bits per
370a61ed2ceSHans Rosenfeld  * potential slot plus one byte that's always used. The maximum number of slots
371a61ed2ceSHans Rosenfeld  * in a device is 256. This means there can be up to 64 bytes worth of data plus
372a61ed2ceSHans Rosenfeld  * the extra byte, so 65 bytes.
373a61ed2ceSHans Rosenfeld  */
374a61ed2ceSHans Rosenfeld #define	CCID_INTR_RESPONSE_SIZE	65
375a61ed2ceSHans Rosenfeld 
376a61ed2ceSHans Rosenfeld /*
377a61ed2ceSHans Rosenfeld  * Minimum and maximum minor ids. We treat the maximum valid 32-bit minor as
378a61ed2ceSHans Rosenfeld  * what we can use due to issues in some file systems and the minors that they
379a61ed2ceSHans Rosenfeld  * can use. We reserved zero as an invalid minor number to make it easier to
380a61ed2ceSHans Rosenfeld  * tell if things have been initialized or not.
381a61ed2ceSHans Rosenfeld  */
382a61ed2ceSHans Rosenfeld #define	CCID_MINOR_MIN		1
383a61ed2ceSHans Rosenfeld #define	CCID_MINOR_MAX		MAXMIN32
384a61ed2ceSHans Rosenfeld #define	CCID_MINOR_INVALID	0
385a61ed2ceSHans Rosenfeld 
386a61ed2ceSHans Rosenfeld /*
387a61ed2ceSHans Rosenfeld  * This value represents the minimum size value that we require in the CCID
388a61ed2ceSHans Rosenfeld  * class descriptor's dwMaxCCIDMessageLength member. We got to 64 bytes based on
389a61ed2ceSHans Rosenfeld  * the required size of a bulk transfer packet size. Especially as many CCID
390a61ed2ceSHans Rosenfeld  * devices are these class of speeds. The specification does require that the
391a61ed2ceSHans Rosenfeld  * minimum size of the dwMaxCCIDMessageLength member is at least the size of its
392a61ed2ceSHans Rosenfeld  * bulk endpoint packet size.
393a61ed2ceSHans Rosenfeld  */
394a61ed2ceSHans Rosenfeld #define	CCID_MIN_MESSAGE_LENGTH	64
395a61ed2ceSHans Rosenfeld 
396a61ed2ceSHans Rosenfeld /*
397a61ed2ceSHans Rosenfeld  * Required forward declarations.
398a61ed2ceSHans Rosenfeld  */
399a61ed2ceSHans Rosenfeld struct ccid;
400a61ed2ceSHans Rosenfeld struct ccid_slot;
401a61ed2ceSHans Rosenfeld struct ccid_minor;
402a61ed2ceSHans Rosenfeld struct ccid_command;
403a61ed2ceSHans Rosenfeld 
404a61ed2ceSHans Rosenfeld /*
405a61ed2ceSHans Rosenfeld  * This structure is used to map between the global set of minor numbers and the
406a61ed2ceSHans Rosenfeld  * things represented by them.
407a61ed2ceSHans Rosenfeld  *
408a61ed2ceSHans Rosenfeld  * We have two different kinds of  minor nodes. The first are CCID slots. The
409a61ed2ceSHans Rosenfeld  * second are cloned opens of those slots. Each of these items has a
410a61ed2ceSHans Rosenfeld  * ccid_minor_idx_t embedded in them that is used to index them in an AVL tree.
411a61ed2ceSHans Rosenfeld  * Given that the number of entries that should be present here is unlikely to
412a61ed2ceSHans Rosenfeld  * be terribly large at any given time, it is hoped that an AVL tree will
413a61ed2ceSHans Rosenfeld  * suffice for now.
414a61ed2ceSHans Rosenfeld  */
415a61ed2ceSHans Rosenfeld typedef struct ccid_minor_idx {
416a61ed2ceSHans Rosenfeld 	id_t cmi_minor;
417a61ed2ceSHans Rosenfeld 	avl_node_t cmi_avl;
418a61ed2ceSHans Rosenfeld 	boolean_t cmi_isslot;
419a61ed2ceSHans Rosenfeld 	union {
420a61ed2ceSHans Rosenfeld 		struct ccid_slot *cmi_slot;
421a61ed2ceSHans Rosenfeld 		struct ccid_minor *cmi_user;
422a61ed2ceSHans Rosenfeld 	} cmi_data;
423a61ed2ceSHans Rosenfeld } ccid_minor_idx_t;
424a61ed2ceSHans Rosenfeld 
425a61ed2ceSHans Rosenfeld typedef enum ccid_minor_flags {
426a61ed2ceSHans Rosenfeld 	CCID_MINOR_F_WAITING		= 1 << 0,
427a61ed2ceSHans Rosenfeld 	CCID_MINOR_F_HAS_EXCL		= 1 << 1,
428a61ed2ceSHans Rosenfeld 	CCID_MINOR_F_TXN_RESET		= 1 << 2,
429a61ed2ceSHans Rosenfeld 	CCID_MINOR_F_READ_WAITING	= 1 << 3,
430a61ed2ceSHans Rosenfeld 	CCID_MINOR_F_WRITABLE		= 1 << 4,
431a61ed2ceSHans Rosenfeld } ccid_minor_flags_t;
432a61ed2ceSHans Rosenfeld 
433a61ed2ceSHans Rosenfeld typedef struct ccid_minor {
434a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t	cm_idx;		/* write-once */
435a61ed2ceSHans Rosenfeld 	cred_t			*cm_opener;	/* write-once */
436a61ed2ceSHans Rosenfeld 	struct ccid_slot	*cm_slot;	/* write-once */
437a61ed2ceSHans Rosenfeld 	list_node_t		cm_minor_list;
438a61ed2ceSHans Rosenfeld 	list_node_t		cm_excl_list;
439a61ed2ceSHans Rosenfeld 	kcondvar_t		cm_read_cv;
440a61ed2ceSHans Rosenfeld 	kcondvar_t		cm_iowait_cv;
441a61ed2ceSHans Rosenfeld 	kcondvar_t		cm_excl_cv;
442a61ed2ceSHans Rosenfeld 	ccid_minor_flags_t	cm_flags;
443a61ed2ceSHans Rosenfeld 	struct pollhead		cm_pollhead;
444a61ed2ceSHans Rosenfeld } ccid_minor_t;
445a61ed2ceSHans Rosenfeld 
446a61ed2ceSHans Rosenfeld typedef enum ccid_slot_flags {
447a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_CHANGED		= 1 << 0,
448a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_INTR_GONE		= 1 << 1,
449a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_INTR_ADD		= 1 << 2,
450a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_PRESENT		= 1 << 3,
451a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_ACTIVE		= 1 << 4,
452a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_NEED_TXN_RESET	= 1 << 5,
453a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_NEED_IO_TEARDOWN	= 1 << 6,
454a61ed2ceSHans Rosenfeld 	CCID_SLOT_F_INTR_OVERCURRENT	= 1 << 7,
455a61ed2ceSHans Rosenfeld } ccid_slot_flags_t;
456a61ed2ceSHans Rosenfeld 
457a61ed2ceSHans Rosenfeld #define	CCID_SLOT_F_INTR_MASK	(CCID_SLOT_F_CHANGED | CCID_SLOT_F_INTR_GONE | \
458a61ed2ceSHans Rosenfeld     CCID_SLOT_F_INTR_ADD)
459a61ed2ceSHans Rosenfeld #define	CCID_SLOT_F_WORK_MASK	(CCID_SLOT_F_INTR_MASK | \
460a61ed2ceSHans Rosenfeld     CCID_SLOT_F_NEED_TXN_RESET | CCID_SLOT_F_INTR_OVERCURRENT)
461a61ed2ceSHans Rosenfeld #define	CCID_SLOT_F_NOEXCL_MASK	(CCID_SLOT_F_NEED_TXN_RESET | \
462a61ed2ceSHans Rosenfeld     CCID_SLOT_F_NEED_IO_TEARDOWN)
463a61ed2ceSHans Rosenfeld 
464a61ed2ceSHans Rosenfeld typedef void (*icc_init_func_t)(struct ccid *, struct ccid_slot *);
465a61ed2ceSHans Rosenfeld typedef int (*icc_transmit_func_t)(struct ccid *, struct ccid_slot *);
466a61ed2ceSHans Rosenfeld typedef void (*icc_complete_func_t)(struct ccid *, struct ccid_slot *,
467a61ed2ceSHans Rosenfeld     struct ccid_command *);
468a61ed2ceSHans Rosenfeld typedef void (*icc_teardown_func_t)(struct ccid *, struct ccid_slot *, int);
469a61ed2ceSHans Rosenfeld typedef void (*icc_fini_func_t)(struct ccid *, struct ccid_slot *);
470a61ed2ceSHans Rosenfeld 
471a61ed2ceSHans Rosenfeld typedef struct ccid_icc {
472a61ed2ceSHans Rosenfeld 	atr_data_t		*icc_atr_data;
473a61ed2ceSHans Rosenfeld 	atr_protocol_t		icc_protocols;
474a61ed2ceSHans Rosenfeld 	atr_protocol_t		icc_cur_protocol;
475a61ed2ceSHans Rosenfeld 	ccid_params_t		icc_params;
476a61ed2ceSHans Rosenfeld 	icc_init_func_t		icc_init;
477a61ed2ceSHans Rosenfeld 	icc_transmit_func_t	icc_tx;
478a61ed2ceSHans Rosenfeld 	icc_complete_func_t	icc_complete;
479a61ed2ceSHans Rosenfeld 	icc_teardown_func_t	icc_teardown;
480a61ed2ceSHans Rosenfeld 	icc_fini_func_t		icc_fini;
481a61ed2ceSHans Rosenfeld } ccid_icc_t;
482a61ed2ceSHans Rosenfeld 
483a61ed2ceSHans Rosenfeld /*
484a61ed2ceSHans Rosenfeld  * Structure used to take care of and map I/O requests and things. This may not
485a61ed2ceSHans Rosenfeld  * make sense as we develop the T=0 and T=1 code.
486a61ed2ceSHans Rosenfeld  */
487a61ed2ceSHans Rosenfeld typedef enum ccid_io_flags {
488a61ed2ceSHans Rosenfeld 	/*
489a61ed2ceSHans Rosenfeld 	 * This flag is used during the period that a thread has started calling
490a61ed2ceSHans Rosenfeld 	 * into ccid_write(9E), but before it has finished queuing up the write.
491a61ed2ceSHans Rosenfeld 	 * This blocks pollout or another thread in write.
492a61ed2ceSHans Rosenfeld 	 */
493a61ed2ceSHans Rosenfeld 	CCID_IO_F_PREPARING	= 1 << 0,
494a61ed2ceSHans Rosenfeld 	/*
495a61ed2ceSHans Rosenfeld 	 * This flag is used once a ccid_write() ICC tx function has
496a61ed2ceSHans Rosenfeld 	 * successfully completed. While this is set, the device is not
497a61ed2ceSHans Rosenfeld 	 * writable; however, it is legal to call ccid_read() and block. This
498a61ed2ceSHans Rosenfeld 	 * flag will remain set until the actual write is done. This indicates
499a61ed2ceSHans Rosenfeld 	 * that the transmission protocol has finished.
500a61ed2ceSHans Rosenfeld 	 */
501a61ed2ceSHans Rosenfeld 	CCID_IO_F_IN_PROGRESS	= 1 << 1,
502a61ed2ceSHans Rosenfeld 	/*
503a61ed2ceSHans Rosenfeld 	 * This flag is used to indicate that the logical I/O has completed in
504a61ed2ceSHans Rosenfeld 	 * one way or the other and that a reader can consume data. When this
505a61ed2ceSHans Rosenfeld 	 * flag is set, then POLLIN | POLLRDNORM should be signaled. Until the
506a61ed2ceSHans Rosenfeld 	 * I/O is consumed via ccid_read(), calls to ccid_write() will fail with
507a61ed2ceSHans Rosenfeld 	 * EBUSY. When this flag is set, the kernel protocol level should be
508a61ed2ceSHans Rosenfeld 	 * idle and it should be safe to tear down.
509a61ed2ceSHans Rosenfeld 	 */
510a61ed2ceSHans Rosenfeld 	CCID_IO_F_DONE		= 1 << 2,
511a61ed2ceSHans Rosenfeld } ccid_io_flags_t;
512a61ed2ceSHans Rosenfeld 
513a61ed2ceSHans Rosenfeld /*
514a61ed2ceSHans Rosenfeld  * If any of the flags in the POLLOUT group are set, then the device is not
515a61ed2ceSHans Rosenfeld  * writeable. The same distinction isn't true for POLLIN. We are only readable
516a61ed2ceSHans Rosenfeld  * if CCID_IO_F_DONE is set. However, you are allowed to call read as soon as
517a61ed2ceSHans Rosenfeld  * CCID_IO_F_IN_PROGRESS is set.
518a61ed2ceSHans Rosenfeld  */
519a61ed2ceSHans Rosenfeld #define	CCID_IO_F_POLLOUT_FLAGS	(CCID_IO_F_PREPARING | CCID_IO_F_IN_PROGRESS | \
520a61ed2ceSHans Rosenfeld     CCID_IO_F_DONE)
521a61ed2ceSHans Rosenfeld #define	CCID_IO_F_ALL_FLAGS	(CCID_IO_F_PREPARING | CCID_IO_F_IN_PROGRESS | \
522a61ed2ceSHans Rosenfeld     CCID_IO_F_DONE | CCID_IO_F_ABANDONED)
523a61ed2ceSHans Rosenfeld 
524a61ed2ceSHans Rosenfeld typedef struct ccid_io {
525a61ed2ceSHans Rosenfeld 	ccid_io_flags_t	ci_flags;
526a61ed2ceSHans Rosenfeld 	size_t		ci_ilen;
527a61ed2ceSHans Rosenfeld 	uint8_t		ci_ibuf[CCID_APDU_LEN_MAX];
528a61ed2ceSHans Rosenfeld 	mblk_t		*ci_omp;
529a61ed2ceSHans Rosenfeld 	kcondvar_t	ci_cv;
530a61ed2ceSHans Rosenfeld 	struct ccid_command *ci_command;
531a61ed2ceSHans Rosenfeld 	int		ci_errno;
532a61ed2ceSHans Rosenfeld 	mblk_t		*ci_data;
533a61ed2ceSHans Rosenfeld } ccid_io_t;
534a61ed2ceSHans Rosenfeld 
535a61ed2ceSHans Rosenfeld typedef struct ccid_slot {
536a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t	cs_idx;		/* WO */
537a61ed2ceSHans Rosenfeld 	uint_t			cs_slotno;	/* WO */
538a61ed2ceSHans Rosenfeld 	struct ccid		*cs_ccid;	/* WO */
539a61ed2ceSHans Rosenfeld 	ccid_slot_flags_t	cs_flags;
540a61ed2ceSHans Rosenfeld 	ccid_class_voltage_t	cs_voltage;
541a61ed2ceSHans Rosenfeld 	mblk_t			*cs_atr;
542a61ed2ceSHans Rosenfeld 	struct ccid_command	*cs_command;
543a61ed2ceSHans Rosenfeld 	ccid_minor_t		*cs_excl_minor;
544a61ed2ceSHans Rosenfeld 	list_t			cs_excl_waiters;
545a61ed2ceSHans Rosenfeld 	list_t			cs_minors;
546a61ed2ceSHans Rosenfeld 	ccid_icc_t		cs_icc;
547a61ed2ceSHans Rosenfeld 	ccid_io_t		cs_io;
548a61ed2ceSHans Rosenfeld } ccid_slot_t;
549a61ed2ceSHans Rosenfeld 
550a61ed2ceSHans Rosenfeld typedef enum ccid_attach_state {
551a61ed2ceSHans Rosenfeld 	CCID_ATTACH_USB_CLIENT	= 1 << 0,
552a61ed2ceSHans Rosenfeld 	CCID_ATTACH_MUTEX_INIT	= 1 << 1,
553a61ed2ceSHans Rosenfeld 	CCID_ATTACH_TASKQ	= 1 << 2,
554a61ed2ceSHans Rosenfeld 	CCID_ATTACH_CMD_LIST	= 1 << 3,
555a61ed2ceSHans Rosenfeld 	CCID_ATTACH_OPEN_PIPES	= 1 << 4,
556a61ed2ceSHans Rosenfeld 	CCID_ATTACH_SEQ_IDS	= 1 << 5,
557a61ed2ceSHans Rosenfeld 	CCID_ATTACH_SLOTS	= 1 << 6,
558a61ed2ceSHans Rosenfeld 	CCID_ATTACH_HOTPLUG_CB	= 1 << 7,
559a61ed2ceSHans Rosenfeld 	CCID_ATTACH_INTR_ACTIVE	= 1 << 8,
560a61ed2ceSHans Rosenfeld 	CCID_ATTACH_MINORS	= 1 << 9,
561a61ed2ceSHans Rosenfeld } ccid_attach_state_t;
562a61ed2ceSHans Rosenfeld 
563a61ed2ceSHans Rosenfeld typedef enum ccid_flags {
564a61ed2ceSHans Rosenfeld 	CCID_F_HAS_INTR		= 1 << 0,
565a61ed2ceSHans Rosenfeld 	CCID_F_NEEDS_PPS	= 1 << 1,
566a61ed2ceSHans Rosenfeld 	CCID_F_NEEDS_PARAMS	= 1 << 2,
567a61ed2ceSHans Rosenfeld 	CCID_F_NEEDS_DATAFREQ	= 1 << 3,
568a61ed2ceSHans Rosenfeld 	CCID_F_DETACHING	= 1 << 5,
569a61ed2ceSHans Rosenfeld 	CCID_F_WORKER_REQUESTED	= 1 << 6,
570a61ed2ceSHans Rosenfeld 	CCID_F_WORKER_RUNNING	= 1 << 7,
571a61ed2ceSHans Rosenfeld 	CCID_F_DISCONNECTED	= 1 << 8
572a61ed2ceSHans Rosenfeld } ccid_flags_t;
573a61ed2ceSHans Rosenfeld 
574a61ed2ceSHans Rosenfeld #define	CCID_F_DEV_GONE_MASK	(CCID_F_DETACHING | CCID_F_DISCONNECTED)
575a61ed2ceSHans Rosenfeld #define	CCID_F_WORKER_MASK	(CCID_F_WORKER_REQUESTED | \
576a61ed2ceSHans Rosenfeld     CCID_F_WORKER_RUNNING)
577a61ed2ceSHans Rosenfeld 
578a61ed2ceSHans Rosenfeld typedef struct ccid_stats {
579a61ed2ceSHans Rosenfeld 	uint64_t	cst_intr_errs;
580a61ed2ceSHans Rosenfeld 	uint64_t	cst_intr_restart;
581a61ed2ceSHans Rosenfeld 	uint64_t	cst_intr_unknown;
582a61ed2ceSHans Rosenfeld 	uint64_t	cst_intr_slot_change;
583a61ed2ceSHans Rosenfeld 	uint64_t	cst_intr_hwerr;
584a61ed2ceSHans Rosenfeld 	uint64_t	cst_intr_inval;
585a61ed2ceSHans Rosenfeld 	uint64_t	cst_ndiscover;
586a61ed2ceSHans Rosenfeld 	hrtime_t	cst_lastdiscover;
587a61ed2ceSHans Rosenfeld } ccid_stats_t;
588a61ed2ceSHans Rosenfeld 
589a61ed2ceSHans Rosenfeld typedef struct ccid {
590a61ed2ceSHans Rosenfeld 	dev_info_t		*ccid_dip;
591a61ed2ceSHans Rosenfeld 	kmutex_t		ccid_mutex;
592a61ed2ceSHans Rosenfeld 	ccid_attach_state_t	ccid_attach;
593a61ed2ceSHans Rosenfeld 	ccid_flags_t		ccid_flags;
594a61ed2ceSHans Rosenfeld 	id_space_t		*ccid_seqs;
595a61ed2ceSHans Rosenfeld 	ddi_taskq_t		*ccid_taskq;
596a61ed2ceSHans Rosenfeld 	usb_client_dev_data_t	*ccid_dev_data;
597a61ed2ceSHans Rosenfeld 	ccid_class_descr_t	ccid_class;		/* WO */
598a61ed2ceSHans Rosenfeld 	usb_ep_xdescr_t		ccid_bulkin_xdesc;	/* WO */
599a61ed2ceSHans Rosenfeld 	usb_pipe_handle_t	ccid_bulkin_pipe;	/* WO */
600a61ed2ceSHans Rosenfeld 	usb_ep_xdescr_t		ccid_bulkout_xdesc;	/* WO */
601a61ed2ceSHans Rosenfeld 	usb_pipe_handle_t	ccid_bulkout_pipe;	/* WO */
602a61ed2ceSHans Rosenfeld 	usb_ep_xdescr_t		ccid_intrin_xdesc;	/* WO */
603a61ed2ceSHans Rosenfeld 	usb_pipe_handle_t	ccid_intrin_pipe;	/* WO */
604a61ed2ceSHans Rosenfeld 	usb_pipe_handle_t	ccid_control_pipe;	/* WO */
605a61ed2ceSHans Rosenfeld 	uint_t			ccid_nslots;		/* WO */
606a61ed2ceSHans Rosenfeld 	size_t			ccid_bufsize;		/* WO */
607a61ed2ceSHans Rosenfeld 	ccid_slot_t		*ccid_slots;
608a61ed2ceSHans Rosenfeld 	timeout_id_t		ccid_poll_timeout;
609a61ed2ceSHans Rosenfeld 	ccid_stats_t		ccid_stats;
610a61ed2ceSHans Rosenfeld 	list_t			ccid_command_queue;
611a61ed2ceSHans Rosenfeld 	list_t			ccid_complete_queue;
612a61ed2ceSHans Rosenfeld 	usb_bulk_req_t		*ccid_bulkin_cache[CCID_BULK_NALLOCED];
613a61ed2ceSHans Rosenfeld 	uint_t			ccid_bulkin_alloced;
614a61ed2ceSHans Rosenfeld 	usb_bulk_req_t		*ccid_bulkin_dispatched;
615a61ed2ceSHans Rosenfeld } ccid_t;
616a61ed2ceSHans Rosenfeld 
617a61ed2ceSHans Rosenfeld /*
618a61ed2ceSHans Rosenfeld  * Command structure for an individual CCID command that we issue to a
619a61ed2ceSHans Rosenfeld  * controller. Note that the command caches a copy of some of the data that's
620a61ed2ceSHans Rosenfeld  * normally inside the CCID header in host-endian fashion.
621a61ed2ceSHans Rosenfeld  */
622a61ed2ceSHans Rosenfeld typedef enum ccid_command_state {
623a61ed2ceSHans Rosenfeld 	CCID_COMMAND_ALLOCATED	= 0x0,
624a61ed2ceSHans Rosenfeld 	CCID_COMMAND_QUEUED,
625a61ed2ceSHans Rosenfeld 	CCID_COMMAND_DISPATCHED,
626a61ed2ceSHans Rosenfeld 	CCID_COMMAND_REPLYING,
627a61ed2ceSHans Rosenfeld 	CCID_COMMAND_COMPLETE,
628a61ed2ceSHans Rosenfeld 	CCID_COMMAND_TRANSPORT_ERROR,
629a61ed2ceSHans Rosenfeld 	CCID_COMMAND_CCID_ABORTED
630a61ed2ceSHans Rosenfeld } ccid_command_state_t;
631a61ed2ceSHans Rosenfeld 
632a61ed2ceSHans Rosenfeld typedef enum ccid_command_flags {
633a61ed2ceSHans Rosenfeld 	CCID_COMMAND_F_USER	= 1 << 0,
634a61ed2ceSHans Rosenfeld } ccid_command_flags_t;
635a61ed2ceSHans Rosenfeld 
636a61ed2ceSHans Rosenfeld typedef struct ccid_command {
637a61ed2ceSHans Rosenfeld 	list_node_t		cc_list_node;
638a61ed2ceSHans Rosenfeld 	kcondvar_t		cc_cv;
639a61ed2ceSHans Rosenfeld 	uint8_t			cc_mtype;
640a61ed2ceSHans Rosenfeld 	ccid_response_code_t	cc_rtype;
641a61ed2ceSHans Rosenfeld 	uint8_t			cc_slot;
642a61ed2ceSHans Rosenfeld 	ccid_command_state_t	cc_state;
643a61ed2ceSHans Rosenfeld 	ccid_command_flags_t	cc_flags;
644a61ed2ceSHans Rosenfeld 	int			cc_usb;
645a61ed2ceSHans Rosenfeld 	usb_cr_t		cc_usbcr;
646a61ed2ceSHans Rosenfeld 	size_t			cc_reqlen;
647a61ed2ceSHans Rosenfeld 	id_t			cc_seq;
648a61ed2ceSHans Rosenfeld 	usb_bulk_req_t		*cc_ubrp;
649a61ed2ceSHans Rosenfeld 	ccid_t			*cc_ccid;
650a61ed2ceSHans Rosenfeld 	hrtime_t		cc_queue_time;
651a61ed2ceSHans Rosenfeld 	hrtime_t		cc_dispatch_time;
652a61ed2ceSHans Rosenfeld 	hrtime_t		cc_dispatch_cb_time;
653a61ed2ceSHans Rosenfeld 	hrtime_t		cc_response_time;
654a61ed2ceSHans Rosenfeld 	hrtime_t		cc_completion_time;
655a61ed2ceSHans Rosenfeld 	mblk_t			*cc_response;
656a61ed2ceSHans Rosenfeld } ccid_command_t;
657a61ed2ceSHans Rosenfeld 
658a61ed2ceSHans Rosenfeld /*
659a61ed2ceSHans Rosenfeld  * ddi_soft_state(9F) pointer. This is used for instances of a CCID controller.
660a61ed2ceSHans Rosenfeld  */
661a61ed2ceSHans Rosenfeld static void *ccid_softstate;
662a61ed2ceSHans Rosenfeld 
663a61ed2ceSHans Rosenfeld /*
664a61ed2ceSHans Rosenfeld  * This is used to keep track of our minor nodes.
665a61ed2ceSHans Rosenfeld  */
666a61ed2ceSHans Rosenfeld static kmutex_t ccid_idxlock;
667a61ed2ceSHans Rosenfeld static avl_tree_t ccid_idx;
668a61ed2ceSHans Rosenfeld static id_space_t *ccid_minors;
669a61ed2ceSHans Rosenfeld 
670a61ed2ceSHans Rosenfeld /*
671a61ed2ceSHans Rosenfeld  * Required Forwards
672a61ed2ceSHans Rosenfeld  */
673a61ed2ceSHans Rosenfeld static void ccid_intr_poll_init(ccid_t *);
674a61ed2ceSHans Rosenfeld static void ccid_worker_request(ccid_t *);
675a61ed2ceSHans Rosenfeld static void ccid_command_dispatch(ccid_t *);
676a61ed2ceSHans Rosenfeld static void ccid_command_free(ccid_command_t *);
677a61ed2ceSHans Rosenfeld static int ccid_bulkin_schedule(ccid_t *);
678a61ed2ceSHans Rosenfeld static void ccid_command_bcopy(ccid_command_t *, const void *, size_t);
679a61ed2ceSHans Rosenfeld 
680a61ed2ceSHans Rosenfeld static int ccid_write_apdu(ccid_t *, ccid_slot_t *);
681a61ed2ceSHans Rosenfeld static void ccid_complete_apdu(ccid_t *, ccid_slot_t *, ccid_command_t *);
682a61ed2ceSHans Rosenfeld static void ccid_teardown_apdu(ccid_t *, ccid_slot_t *, int);
683a61ed2ceSHans Rosenfeld 
684a61ed2ceSHans Rosenfeld 
685a61ed2ceSHans Rosenfeld static int
ccid_idx_comparator(const void * l,const void * r)686a61ed2ceSHans Rosenfeld ccid_idx_comparator(const void *l, const void *r)
687a61ed2ceSHans Rosenfeld {
688a61ed2ceSHans Rosenfeld 	const ccid_minor_idx_t *lc = l, *rc = r;
689a61ed2ceSHans Rosenfeld 
690a61ed2ceSHans Rosenfeld 	if (lc->cmi_minor > rc->cmi_minor)
691a61ed2ceSHans Rosenfeld 		return (1);
692a61ed2ceSHans Rosenfeld 	if (lc->cmi_minor < rc->cmi_minor)
693a61ed2ceSHans Rosenfeld 		return (-1);
694a61ed2ceSHans Rosenfeld 	return (0);
695a61ed2ceSHans Rosenfeld }
696a61ed2ceSHans Rosenfeld 
697a61ed2ceSHans Rosenfeld static void
ccid_error(ccid_t * ccid,const char * fmt,...)698a61ed2ceSHans Rosenfeld ccid_error(ccid_t *ccid, const char *fmt, ...)
699a61ed2ceSHans Rosenfeld {
700a61ed2ceSHans Rosenfeld 	va_list ap;
701a61ed2ceSHans Rosenfeld 
702a61ed2ceSHans Rosenfeld 	va_start(ap, fmt);
703a61ed2ceSHans Rosenfeld 	if (ccid != NULL) {
704a61ed2ceSHans Rosenfeld 		vdev_err(ccid->ccid_dip, CE_WARN, fmt, ap);
705a61ed2ceSHans Rosenfeld 	} else {
706a61ed2ceSHans Rosenfeld 		vcmn_err(CE_WARN, fmt, ap);
707a61ed2ceSHans Rosenfeld 	}
708a61ed2ceSHans Rosenfeld 	va_end(ap);
709a61ed2ceSHans Rosenfeld }
710a61ed2ceSHans Rosenfeld 
711a61ed2ceSHans Rosenfeld static void
ccid_minor_idx_free(ccid_minor_idx_t * idx)712a61ed2ceSHans Rosenfeld ccid_minor_idx_free(ccid_minor_idx_t *idx)
713a61ed2ceSHans Rosenfeld {
714a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *ip;
715a61ed2ceSHans Rosenfeld 
716a61ed2ceSHans Rosenfeld 	VERIFY3S(idx->cmi_minor, !=, CCID_MINOR_INVALID);
717a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid_idxlock);
718a61ed2ceSHans Rosenfeld 	ip = avl_find(&ccid_idx, idx, NULL);
719a61ed2ceSHans Rosenfeld 	VERIFY3P(idx, ==, ip);
720a61ed2ceSHans Rosenfeld 	avl_remove(&ccid_idx, idx);
721a61ed2ceSHans Rosenfeld 	id_free(ccid_minors, idx->cmi_minor);
722a61ed2ceSHans Rosenfeld 	idx->cmi_minor = CCID_MINOR_INVALID;
723a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid_idxlock);
724a61ed2ceSHans Rosenfeld }
725a61ed2ceSHans Rosenfeld 
726a61ed2ceSHans Rosenfeld static boolean_t
ccid_minor_idx_alloc(ccid_minor_idx_t * idx,boolean_t sleep)727a61ed2ceSHans Rosenfeld ccid_minor_idx_alloc(ccid_minor_idx_t *idx, boolean_t sleep)
728a61ed2ceSHans Rosenfeld {
729a61ed2ceSHans Rosenfeld 	id_t id;
730a61ed2ceSHans Rosenfeld 
731a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid_idxlock);
732a61ed2ceSHans Rosenfeld 	if (sleep) {
733a61ed2ceSHans Rosenfeld 		id = id_alloc(ccid_minors);
734a61ed2ceSHans Rosenfeld 	} else {
735a61ed2ceSHans Rosenfeld 		id = id_alloc_nosleep(ccid_minors);
736a61ed2ceSHans Rosenfeld 	}
737a61ed2ceSHans Rosenfeld 	if (id == -1) {
738a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid_idxlock);
739a61ed2ceSHans Rosenfeld 		return (B_FALSE);
740a61ed2ceSHans Rosenfeld 	}
741a61ed2ceSHans Rosenfeld 	idx->cmi_minor = id;
742a61ed2ceSHans Rosenfeld 	avl_add(&ccid_idx, idx);
743a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid_idxlock);
744a61ed2ceSHans Rosenfeld 
745a61ed2ceSHans Rosenfeld 	return (B_TRUE);
746a61ed2ceSHans Rosenfeld }
747a61ed2ceSHans Rosenfeld 
748a61ed2ceSHans Rosenfeld static ccid_minor_idx_t *
ccid_minor_find(minor_t m)749a61ed2ceSHans Rosenfeld ccid_minor_find(minor_t m)
750a61ed2ceSHans Rosenfeld {
751a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t i = { 0 };
752a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *ret;
753a61ed2ceSHans Rosenfeld 
754a61ed2ceSHans Rosenfeld 	i.cmi_minor = m;
755a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid_idxlock);
756a61ed2ceSHans Rosenfeld 	ret = avl_find(&ccid_idx, &i, NULL);
757a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid_idxlock);
758a61ed2ceSHans Rosenfeld 
759a61ed2ceSHans Rosenfeld 	return (ret);
760a61ed2ceSHans Rosenfeld }
761a61ed2ceSHans Rosenfeld 
762a61ed2ceSHans Rosenfeld static ccid_minor_idx_t *
ccid_minor_find_user(minor_t m)763a61ed2ceSHans Rosenfeld ccid_minor_find_user(minor_t m)
764a61ed2ceSHans Rosenfeld {
765a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *idx;
766a61ed2ceSHans Rosenfeld 
767a61ed2ceSHans Rosenfeld 	idx = ccid_minor_find(m);
768a61ed2ceSHans Rosenfeld 	if (idx == NULL) {
769a61ed2ceSHans Rosenfeld 		return (NULL);
770a61ed2ceSHans Rosenfeld 	}
771a61ed2ceSHans Rosenfeld 	VERIFY0(idx->cmi_isslot);
772a61ed2ceSHans Rosenfeld 	return (idx);
773a61ed2ceSHans Rosenfeld }
774a61ed2ceSHans Rosenfeld 
775a61ed2ceSHans Rosenfeld static void
ccid_clear_io(ccid_io_t * io)776a61ed2ceSHans Rosenfeld ccid_clear_io(ccid_io_t *io)
777a61ed2ceSHans Rosenfeld {
778a61ed2ceSHans Rosenfeld 	freemsg(io->ci_data);
779a61ed2ceSHans Rosenfeld 	io->ci_data = NULL;
780a61ed2ceSHans Rosenfeld 	io->ci_errno = 0;
781a61ed2ceSHans Rosenfeld 	io->ci_flags &= ~CCID_IO_F_DONE;
782a61ed2ceSHans Rosenfeld 	io->ci_ilen = 0;
783a61ed2ceSHans Rosenfeld 	bzero(io->ci_ibuf, sizeof (io->ci_ibuf));
784a61ed2ceSHans Rosenfeld }
785a61ed2ceSHans Rosenfeld 
786a61ed2ceSHans Rosenfeld /*
787a61ed2ceSHans Rosenfeld  * Check if the conditions are met to signal the next exclusive holder. For this
788a61ed2ceSHans Rosenfeld  * to be true, there should be no one holding it. In addition, there must be
789a61ed2ceSHans Rosenfeld  * someone in the queue waiting. Finally, we want to make sure that the ICC, if
790a61ed2ceSHans Rosenfeld  * present, is in a state where it could handle these kinds of issues. That
791a61ed2ceSHans Rosenfeld  * means that we shouldn't have an outstanding I/O question or warm reset
792a61ed2ceSHans Rosenfeld  * ongoing. However, we must not block this on the condition of an ICC being
793a61ed2ceSHans Rosenfeld  * present. But, if the reader has been disconnected, don't signal anyone.
794a61ed2ceSHans Rosenfeld  */
795a61ed2ceSHans Rosenfeld static void
ccid_slot_excl_maybe_signal(ccid_slot_t * slot)796a61ed2ceSHans Rosenfeld ccid_slot_excl_maybe_signal(ccid_slot_t *slot)
797a61ed2ceSHans Rosenfeld {
798a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
799a61ed2ceSHans Rosenfeld 
800a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&slot->cs_ccid->ccid_mutex));
801a61ed2ceSHans Rosenfeld 
802a61ed2ceSHans Rosenfeld 	if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0)
803a61ed2ceSHans Rosenfeld 		return;
804a61ed2ceSHans Rosenfeld 	if (slot->cs_excl_minor != NULL)
805a61ed2ceSHans Rosenfeld 		return;
806a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK) != 0)
807a61ed2ceSHans Rosenfeld 		return;
808a61ed2ceSHans Rosenfeld 	cmp = list_head(&slot->cs_excl_waiters);
809a61ed2ceSHans Rosenfeld 	if (cmp == NULL)
810a61ed2ceSHans Rosenfeld 		return;
811a61ed2ceSHans Rosenfeld 	cv_signal(&cmp->cm_excl_cv);
812a61ed2ceSHans Rosenfeld }
813a61ed2ceSHans Rosenfeld 
814a61ed2ceSHans Rosenfeld static void
ccid_slot_excl_rele(ccid_slot_t * slot)815a61ed2ceSHans Rosenfeld ccid_slot_excl_rele(ccid_slot_t *slot)
816a61ed2ceSHans Rosenfeld {
817a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
818a61ed2ceSHans Rosenfeld 	ccid_t *ccid = slot->cs_ccid;
819a61ed2ceSHans Rosenfeld 
820a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
821a61ed2ceSHans Rosenfeld 	VERIFY3P(slot->cs_excl_minor, !=, NULL);
822a61ed2ceSHans Rosenfeld 
823a61ed2ceSHans Rosenfeld 	cmp = slot->cs_excl_minor;
824a61ed2ceSHans Rosenfeld 
825a61ed2ceSHans Rosenfeld 	/*
826a61ed2ceSHans Rosenfeld 	 * If we have an outstanding command left by the user when they've
827a61ed2ceSHans Rosenfeld 	 * closed the slot, we need to clean up this command. We need to call
828a61ed2ceSHans Rosenfeld 	 * the protocol specific handler here to determine what to do. If the
829a61ed2ceSHans Rosenfeld 	 * command has completed, but the user has never called read, then it
830a61ed2ceSHans Rosenfeld 	 * will simply clean it up. Otherwise it will indicate that there is
831a61ed2ceSHans Rosenfeld 	 * some amount of external state still ongoing to take care of and clean
832a61ed2ceSHans Rosenfeld 	 * up later.
833a61ed2ceSHans Rosenfeld 	 */
834a61ed2ceSHans Rosenfeld 	if (slot->cs_icc.icc_teardown != NULL) {
835a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_teardown(ccid, slot, ECANCELED);
836a61ed2ceSHans Rosenfeld 	}
837a61ed2ceSHans Rosenfeld 
838a61ed2ceSHans Rosenfeld 	/*
839a61ed2ceSHans Rosenfeld 	 * There may either be a thread blocked in read or in the process of
840a61ed2ceSHans Rosenfeld 	 * preparing a write. In either case, we need to make sure that they're
841a61ed2ceSHans Rosenfeld 	 * woken up or finish, before we finish tear down.
842a61ed2ceSHans Rosenfeld 	 */
843a61ed2ceSHans Rosenfeld 	while ((cmp->cm_flags & CCID_MINOR_F_READ_WAITING) != 0 ||
844a61ed2ceSHans Rosenfeld 	    (slot->cs_io.ci_flags & CCID_IO_F_PREPARING) != 0) {
845a61ed2ceSHans Rosenfeld 		cv_wait(&cmp->cm_iowait_cv, &ccid->ccid_mutex);
846a61ed2ceSHans Rosenfeld 	}
847a61ed2ceSHans Rosenfeld 
848a61ed2ceSHans Rosenfeld 	/*
849a61ed2ceSHans Rosenfeld 	 * At this point, we hold the lock and there should be no other threads
850a61ed2ceSHans Rosenfeld 	 * that are past the basic sanity checks. So at this point, note that
851a61ed2ceSHans Rosenfeld 	 * this minor no longer has exclusive access (causing other read/write
852a61ed2ceSHans Rosenfeld 	 * calls to fail) and start the process of cleaning up the outstanding
853a61ed2ceSHans Rosenfeld 	 * I/O on the slot. It is OK that at this point the thread may try to
854a61ed2ceSHans Rosenfeld 	 * obtain exclusive access again. It will end up blocking on everything
855a61ed2ceSHans Rosenfeld 	 * else.
856a61ed2ceSHans Rosenfeld 	 */
857a61ed2ceSHans Rosenfeld 	cmp->cm_flags &= ~CCID_MINOR_F_HAS_EXCL;
858a61ed2ceSHans Rosenfeld 	slot->cs_excl_minor = NULL;
859a61ed2ceSHans Rosenfeld 
860a61ed2ceSHans Rosenfeld 	/*
861a61ed2ceSHans Rosenfeld 	 * If at this point, we have an I/O that's noted as being done, but no
862a61ed2ceSHans Rosenfeld 	 * one blocked in read, then we need to clean that up. The ICC teardown
863a61ed2ceSHans Rosenfeld 	 * function is only designed to take care of in-flight I/Os.
864a61ed2ceSHans Rosenfeld 	 */
865a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) != 0)
866a61ed2ceSHans Rosenfeld 		ccid_clear_io(&slot->cs_io);
867a61ed2ceSHans Rosenfeld 
868a61ed2ceSHans Rosenfeld 	/*
869a61ed2ceSHans Rosenfeld 	 * Regardless of when we're polling, we need to go through and error
870a61ed2ceSHans Rosenfeld 	 * out.
871a61ed2ceSHans Rosenfeld 	 */
872a61ed2ceSHans Rosenfeld 	pollwakeup(&cmp->cm_pollhead, POLLERR);
873a61ed2ceSHans Rosenfeld 
874a61ed2ceSHans Rosenfeld 	/*
875a61ed2ceSHans Rosenfeld 	 * If we've been asked to reset the card before handing it off, schedule
876a61ed2ceSHans Rosenfeld 	 * that. Otherwise, allow the next entry in the queue to get woken up
877a61ed2ceSHans Rosenfeld 	 * and given access to the card.
878a61ed2ceSHans Rosenfeld 	 */
879a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_TXN_RESET) != 0) {
880a61ed2ceSHans Rosenfeld 		slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
881a61ed2ceSHans Rosenfeld 		ccid_worker_request(ccid);
882a61ed2ceSHans Rosenfeld 		cmp->cm_flags &= ~CCID_MINOR_F_TXN_RESET;
883a61ed2ceSHans Rosenfeld 	} else {
884a61ed2ceSHans Rosenfeld 		ccid_slot_excl_maybe_signal(slot);
885a61ed2ceSHans Rosenfeld 	}
886a61ed2ceSHans Rosenfeld }
887a61ed2ceSHans Rosenfeld 
888a61ed2ceSHans Rosenfeld static int
ccid_slot_excl_req(ccid_slot_t * slot,ccid_minor_t * cmp,boolean_t nosleep)889a61ed2ceSHans Rosenfeld ccid_slot_excl_req(ccid_slot_t *slot, ccid_minor_t *cmp, boolean_t nosleep)
890a61ed2ceSHans Rosenfeld {
891a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&slot->cs_ccid->ccid_mutex));
892a61ed2ceSHans Rosenfeld 
893a61ed2ceSHans Rosenfeld 	if (slot->cs_excl_minor == cmp) {
894a61ed2ceSHans Rosenfeld 		VERIFY((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) != 0);
895a61ed2ceSHans Rosenfeld 		return (EEXIST);
896a61ed2ceSHans Rosenfeld 	}
897a61ed2ceSHans Rosenfeld 
898a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_WAITING) != 0) {
899a61ed2ceSHans Rosenfeld 		return (EINPROGRESS);
900a61ed2ceSHans Rosenfeld 	}
901a61ed2ceSHans Rosenfeld 
902a61ed2ceSHans Rosenfeld 	/*
903a61ed2ceSHans Rosenfeld 	 * If we were asked to try and fail quickly, do that before the main
904a61ed2ceSHans Rosenfeld 	 * loop.
905a61ed2ceSHans Rosenfeld 	 */
906a61ed2ceSHans Rosenfeld 	if (nosleep && slot->cs_excl_minor != NULL &&
907a61ed2ceSHans Rosenfeld 	    (slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK) == 0) {
908a61ed2ceSHans Rosenfeld 		return (EBUSY);
909a61ed2ceSHans Rosenfeld 	}
910a61ed2ceSHans Rosenfeld 
911a61ed2ceSHans Rosenfeld 	/*
912a61ed2ceSHans Rosenfeld 	 * Mark that we're waiting in case we race with another thread trying to
913a61ed2ceSHans Rosenfeld 	 * claim exclusive access for this. Insert ourselves on the wait list.
914a61ed2ceSHans Rosenfeld 	 * If for some reason we get a signal, then we can't know for certain if
915a61ed2ceSHans Rosenfeld 	 * we had a signal / cv race. In such a case, we always wake up the
916a61ed2ceSHans Rosenfeld 	 * next person in the queue (potentially spuriously).
917a61ed2ceSHans Rosenfeld 	 */
918a61ed2ceSHans Rosenfeld 	cmp->cm_flags |= CCID_MINOR_F_WAITING;
919a61ed2ceSHans Rosenfeld 	list_insert_tail(&slot->cs_excl_waiters, cmp);
920a61ed2ceSHans Rosenfeld 	while (slot->cs_excl_minor != NULL ||
921a61ed2ceSHans Rosenfeld 	    (slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK) != 0) {
922a61ed2ceSHans Rosenfeld 		if (cv_wait_sig(&cmp->cm_excl_cv, &slot->cs_ccid->ccid_mutex) ==
923a61ed2ceSHans Rosenfeld 		    0) {
924a61ed2ceSHans Rosenfeld 			/*
925a61ed2ceSHans Rosenfeld 			 * Remove ourselves from the list and try to signal the
926a61ed2ceSHans Rosenfeld 			 * next thread.
927a61ed2ceSHans Rosenfeld 			 */
928a61ed2ceSHans Rosenfeld 			list_remove(&slot->cs_excl_waiters, cmp);
929a61ed2ceSHans Rosenfeld 			cmp->cm_flags &= ~CCID_MINOR_F_WAITING;
930a61ed2ceSHans Rosenfeld 			ccid_slot_excl_maybe_signal(slot);
931a61ed2ceSHans Rosenfeld 			return (EINTR);
932a61ed2ceSHans Rosenfeld 		}
933a61ed2ceSHans Rosenfeld 
934a61ed2ceSHans Rosenfeld 		/*
935a61ed2ceSHans Rosenfeld 		 * Check if the reader is going away. If so, then we're done
936a61ed2ceSHans Rosenfeld 		 * here.
937a61ed2ceSHans Rosenfeld 		 */
938a61ed2ceSHans Rosenfeld 		if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
939a61ed2ceSHans Rosenfeld 			list_remove(&slot->cs_excl_waiters, cmp);
940a61ed2ceSHans Rosenfeld 			cmp->cm_flags &= ~CCID_MINOR_F_WAITING;
941a61ed2ceSHans Rosenfeld 			return (ENODEV);
942a61ed2ceSHans Rosenfeld 		}
943a61ed2ceSHans Rosenfeld 	}
944a61ed2ceSHans Rosenfeld 
945a61ed2ceSHans Rosenfeld 	VERIFY0(slot->cs_flags & CCID_SLOT_F_NOEXCL_MASK);
946a61ed2ceSHans Rosenfeld 	list_remove(&slot->cs_excl_waiters, cmp);
947a61ed2ceSHans Rosenfeld 
948a61ed2ceSHans Rosenfeld 	cmp->cm_flags &= ~CCID_MINOR_F_WAITING;
949a61ed2ceSHans Rosenfeld 	cmp->cm_flags |= CCID_MINOR_F_HAS_EXCL;
950a61ed2ceSHans Rosenfeld 	slot->cs_excl_minor = cmp;
951a61ed2ceSHans Rosenfeld 	return (0);
952a61ed2ceSHans Rosenfeld }
953a61ed2ceSHans Rosenfeld 
954a61ed2ceSHans Rosenfeld /*
955a61ed2ceSHans Rosenfeld  * Check whether or not we're in a state that we can signal a POLLIN. To be able
956a61ed2ceSHans Rosenfeld  * to signal a POLLIN (meaning that we can read) the following must be true:
957a61ed2ceSHans Rosenfeld  *
958a61ed2ceSHans Rosenfeld  *   o There is a client that has an exclusive hold open
959a61ed2ceSHans Rosenfeld  *   o There is a data which is readable by the client (an I/O is done).
960a61ed2ceSHans Rosenfeld  *
961a61ed2ceSHans Rosenfeld  * Unlike with pollout, we don't care about the state of the ICC.
962a61ed2ceSHans Rosenfeld  */
963a61ed2ceSHans Rosenfeld static void
ccid_slot_pollin_signal(ccid_slot_t * slot)964a61ed2ceSHans Rosenfeld ccid_slot_pollin_signal(ccid_slot_t *slot)
965a61ed2ceSHans Rosenfeld {
966a61ed2ceSHans Rosenfeld 	ccid_t *ccid = slot->cs_ccid;
967a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp = slot->cs_excl_minor;
968a61ed2ceSHans Rosenfeld 
969a61ed2ceSHans Rosenfeld 	if (cmp == NULL)
970a61ed2ceSHans Rosenfeld 		return;
971a61ed2ceSHans Rosenfeld 
972a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
973a61ed2ceSHans Rosenfeld 
974a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) == 0)
975a61ed2ceSHans Rosenfeld 		return;
976a61ed2ceSHans Rosenfeld 
977a61ed2ceSHans Rosenfeld 	pollwakeup(&cmp->cm_pollhead, POLLIN | POLLRDNORM);
978a61ed2ceSHans Rosenfeld }
979a61ed2ceSHans Rosenfeld 
980a61ed2ceSHans Rosenfeld /*
981a61ed2ceSHans Rosenfeld  * Check whether or not we're in a state that we can signal a POLLOUT. To be
982a61ed2ceSHans Rosenfeld  * able to signal a POLLOUT (meaning that we can write) the following must be
983a61ed2ceSHans Rosenfeld  * true:
984a61ed2ceSHans Rosenfeld  *
985a61ed2ceSHans Rosenfeld  *   o There is a minor which has an exclusive hold on the device
986a61ed2ceSHans Rosenfeld  *   o There is no outstanding I/O activity going on, meaning that there is no
987a61ed2ceSHans Rosenfeld  *     operation in progress and any write data has been consumed.
988a61ed2ceSHans Rosenfeld  *   o There is an ICC present
989a61ed2ceSHans Rosenfeld  *   o There is no outstanding I/O cleanup being done, whether a T=1 abort, a
990a61ed2ceSHans Rosenfeld  *     warm reset, or something else.
991a61ed2ceSHans Rosenfeld  */
992a61ed2ceSHans Rosenfeld static void
ccid_slot_pollout_signal(ccid_slot_t * slot)993a61ed2ceSHans Rosenfeld ccid_slot_pollout_signal(ccid_slot_t *slot)
994a61ed2ceSHans Rosenfeld {
995a61ed2ceSHans Rosenfeld 	ccid_t *ccid = slot->cs_ccid;
996a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp = slot->cs_excl_minor;
997a61ed2ceSHans Rosenfeld 
998a61ed2ceSHans Rosenfeld 	if (cmp == NULL)
999a61ed2ceSHans Rosenfeld 		return;
1000a61ed2ceSHans Rosenfeld 
1001a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
1002a61ed2ceSHans Rosenfeld 
1003a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & CCID_IO_F_POLLOUT_FLAGS) != 0 ||
1004a61ed2ceSHans Rosenfeld 	    (slot->cs_flags & CCID_SLOT_F_ACTIVE) == 0 ||
1005a61ed2ceSHans Rosenfeld 	    (ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0 ||
1006a61ed2ceSHans Rosenfeld 	    (slot->cs_flags & CCID_SLOT_F_NEED_IO_TEARDOWN) != 0)
1007a61ed2ceSHans Rosenfeld 		return;
1008a61ed2ceSHans Rosenfeld 
1009a61ed2ceSHans Rosenfeld 	pollwakeup(&cmp->cm_pollhead, POLLOUT);
1010a61ed2ceSHans Rosenfeld }
1011a61ed2ceSHans Rosenfeld 
1012a61ed2ceSHans Rosenfeld static void
ccid_slot_io_teardown_done(ccid_slot_t * slot)1013a61ed2ceSHans Rosenfeld ccid_slot_io_teardown_done(ccid_slot_t *slot)
1014a61ed2ceSHans Rosenfeld {
1015a61ed2ceSHans Rosenfeld 	ccid_t *ccid = slot->cs_ccid;
1016a61ed2ceSHans Rosenfeld 
1017a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
1018a61ed2ceSHans Rosenfeld 	slot->cs_flags &= ~CCID_SLOT_F_NEED_IO_TEARDOWN;
1019a61ed2ceSHans Rosenfeld 	cv_broadcast(&slot->cs_io.ci_cv);
1020a61ed2ceSHans Rosenfeld 
1021a61ed2ceSHans Rosenfeld 	ccid_slot_pollout_signal(slot);
1022a61ed2ceSHans Rosenfeld }
1023a61ed2ceSHans Rosenfeld 
1024a61ed2ceSHans Rosenfeld /*
1025a61ed2ceSHans Rosenfeld  * This will probably need to change when we start doing TPDU processing.
1026a61ed2ceSHans Rosenfeld  */
1027a61ed2ceSHans Rosenfeld static size_t
ccid_command_resp_length(ccid_command_t * cc)1028a61ed2ceSHans Rosenfeld ccid_command_resp_length(ccid_command_t *cc)
1029a61ed2ceSHans Rosenfeld {
1030a61ed2ceSHans Rosenfeld 	uint32_t len;
1031a61ed2ceSHans Rosenfeld 	const ccid_header_t *cch;
1032a61ed2ceSHans Rosenfeld 
1033a61ed2ceSHans Rosenfeld 	VERIFY3P(cc, !=, NULL);
1034a61ed2ceSHans Rosenfeld 	VERIFY3P(cc->cc_response, !=, NULL);
1035a61ed2ceSHans Rosenfeld 
1036a61ed2ceSHans Rosenfeld 	/*
1037a61ed2ceSHans Rosenfeld 	 * Fetch out an arbitrarily aligned LE uint32_t value from the header.
1038a61ed2ceSHans Rosenfeld 	 */
1039a61ed2ceSHans Rosenfeld 	cch = (ccid_header_t *)cc->cc_response->b_rptr;
1040a61ed2ceSHans Rosenfeld 	bcopy(&cch->ch_length, &len, sizeof (len));
1041a61ed2ceSHans Rosenfeld 	len = LE_32(len);
1042a61ed2ceSHans Rosenfeld 	return (len);
1043a61ed2ceSHans Rosenfeld }
1044a61ed2ceSHans Rosenfeld 
1045a61ed2ceSHans Rosenfeld static uint8_t
ccid_command_resp_param2(ccid_command_t * cc)1046a61ed2ceSHans Rosenfeld ccid_command_resp_param2(ccid_command_t *cc)
1047a61ed2ceSHans Rosenfeld {
1048a61ed2ceSHans Rosenfeld 	const ccid_header_t *cch;
1049a61ed2ceSHans Rosenfeld 	uint8_t val;
1050a61ed2ceSHans Rosenfeld 
1051a61ed2ceSHans Rosenfeld 	VERIFY3P(cc, !=, NULL);
1052a61ed2ceSHans Rosenfeld 	VERIFY3P(cc->cc_response, !=, NULL);
1053a61ed2ceSHans Rosenfeld 
1054a61ed2ceSHans Rosenfeld 	cch = (ccid_header_t *)cc->cc_response->b_rptr;
1055a61ed2ceSHans Rosenfeld 	bcopy(&cch->ch_param2, &val, sizeof (val));
1056a61ed2ceSHans Rosenfeld 	return (val);
1057a61ed2ceSHans Rosenfeld }
1058a61ed2ceSHans Rosenfeld 
1059a61ed2ceSHans Rosenfeld /*
1060a61ed2ceSHans Rosenfeld  * Complete a single command. The way that a command completes depends on the
1061a61ed2ceSHans Rosenfeld  * kind of command that occurs. If this command is flagged as a user command,
1062a61ed2ceSHans Rosenfeld  * that implies that it must be handled in a different way from administrative
1063a61ed2ceSHans Rosenfeld  * commands. User commands are placed into the minor to consume via a read(9E).
1064a61ed2ceSHans Rosenfeld  * Non-user commands are placed into a completion queue and must be picked up
1065a61ed2ceSHans Rosenfeld  * via the ccid_command_poll() interface.
1066a61ed2ceSHans Rosenfeld  */
1067a61ed2ceSHans Rosenfeld static void
ccid_command_complete(ccid_command_t * cc)1068a61ed2ceSHans Rosenfeld ccid_command_complete(ccid_command_t *cc)
1069a61ed2ceSHans Rosenfeld {
1070a61ed2ceSHans Rosenfeld 	ccid_t *ccid = cc->cc_ccid;
1071a61ed2ceSHans Rosenfeld 
1072a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
1073a61ed2ceSHans Rosenfeld 	cc->cc_completion_time = gethrtime();
1074a61ed2ceSHans Rosenfeld 	list_remove(&ccid->ccid_command_queue, cc);
1075a61ed2ceSHans Rosenfeld 
1076a61ed2ceSHans Rosenfeld 	if (cc->cc_flags & CCID_COMMAND_F_USER) {
1077a61ed2ceSHans Rosenfeld 		ccid_slot_t *slot;
1078a61ed2ceSHans Rosenfeld 
1079a61ed2ceSHans Rosenfeld 		slot = &ccid->ccid_slots[cc->cc_slot];
1080a61ed2ceSHans Rosenfeld 		ASSERT3P(slot->cs_icc.icc_complete, !=, NULL);
1081a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_complete(ccid, slot, cc);
1082a61ed2ceSHans Rosenfeld 	} else {
1083a61ed2ceSHans Rosenfeld 		list_insert_tail(&ccid->ccid_complete_queue, cc);
1084a61ed2ceSHans Rosenfeld 		cv_broadcast(&cc->cc_cv);
1085a61ed2ceSHans Rosenfeld 	}
1086a61ed2ceSHans Rosenfeld 
1087a61ed2ceSHans Rosenfeld 	/*
1088a61ed2ceSHans Rosenfeld 	 * Finally, we also need to kick off the next command.
1089a61ed2ceSHans Rosenfeld 	 */
1090a61ed2ceSHans Rosenfeld 	ccid_command_dispatch(ccid);
1091a61ed2ceSHans Rosenfeld }
1092a61ed2ceSHans Rosenfeld 
1093a61ed2ceSHans Rosenfeld static void
ccid_command_state_transition(ccid_command_t * cc,ccid_command_state_t state)1094a61ed2ceSHans Rosenfeld ccid_command_state_transition(ccid_command_t *cc, ccid_command_state_t state)
1095a61ed2ceSHans Rosenfeld {
1096a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&cc->cc_ccid->ccid_mutex));
1097a61ed2ceSHans Rosenfeld 
1098a61ed2ceSHans Rosenfeld 	cc->cc_state = state;
1099a61ed2ceSHans Rosenfeld 	cv_broadcast(&cc->cc_cv);
1100a61ed2ceSHans Rosenfeld }
1101a61ed2ceSHans Rosenfeld 
1102a61ed2ceSHans Rosenfeld static void
ccid_command_transport_error(ccid_command_t * cc,int usb_status,usb_cr_t cr)1103a61ed2ceSHans Rosenfeld ccid_command_transport_error(ccid_command_t *cc, int usb_status, usb_cr_t cr)
1104a61ed2ceSHans Rosenfeld {
1105a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&cc->cc_ccid->ccid_mutex));
1106a61ed2ceSHans Rosenfeld 
1107a61ed2ceSHans Rosenfeld 	ccid_command_state_transition(cc, CCID_COMMAND_TRANSPORT_ERROR);
1108a61ed2ceSHans Rosenfeld 	cc->cc_usb = usb_status;
1109a61ed2ceSHans Rosenfeld 	cc->cc_usbcr = cr;
1110a61ed2ceSHans Rosenfeld 	cc->cc_response = NULL;
1111a61ed2ceSHans Rosenfeld 
1112a61ed2ceSHans Rosenfeld 	ccid_command_complete(cc);
1113a61ed2ceSHans Rosenfeld }
1114a61ed2ceSHans Rosenfeld 
1115a61ed2ceSHans Rosenfeld static void
ccid_command_status_decode(ccid_command_t * cc,ccid_reply_command_status_t * comp,ccid_reply_icc_status_t * iccp,ccid_command_err_t * errp)1116a61ed2ceSHans Rosenfeld ccid_command_status_decode(ccid_command_t *cc,
1117a61ed2ceSHans Rosenfeld     ccid_reply_command_status_t *comp, ccid_reply_icc_status_t *iccp,
1118a61ed2ceSHans Rosenfeld     ccid_command_err_t *errp)
1119a61ed2ceSHans Rosenfeld {
1120a61ed2ceSHans Rosenfeld 	ccid_header_t cch;
1121a61ed2ceSHans Rosenfeld 	size_t mblen;
1122a61ed2ceSHans Rosenfeld 
1123a61ed2ceSHans Rosenfeld 	VERIFY3S(cc->cc_state, ==, CCID_COMMAND_COMPLETE);
1124a61ed2ceSHans Rosenfeld 	VERIFY3P(cc->cc_response, !=, NULL);
1125a61ed2ceSHans Rosenfeld 	mblen = msgsize(cc->cc_response);
1126a61ed2ceSHans Rosenfeld 	VERIFY3U(mblen, >=, sizeof (cch));
1127a61ed2ceSHans Rosenfeld 
1128a61ed2ceSHans Rosenfeld 	bcopy(cc->cc_response->b_rptr, &cch, sizeof (cch));
1129a61ed2ceSHans Rosenfeld 	if (comp != NULL) {
1130a61ed2ceSHans Rosenfeld 		*comp = CCID_REPLY_STATUS(cch.ch_param0);
1131a61ed2ceSHans Rosenfeld 	}
1132a61ed2ceSHans Rosenfeld 
1133a61ed2ceSHans Rosenfeld 	if (iccp != NULL) {
1134a61ed2ceSHans Rosenfeld 		*iccp = CCID_REPLY_ICC(cch.ch_param0);
1135a61ed2ceSHans Rosenfeld 	}
1136a61ed2ceSHans Rosenfeld 
1137a61ed2ceSHans Rosenfeld 	if (errp != NULL) {
1138a61ed2ceSHans Rosenfeld 		*errp = cch.ch_param1;
1139a61ed2ceSHans Rosenfeld 	}
1140a61ed2ceSHans Rosenfeld }
1141a61ed2ceSHans Rosenfeld 
1142a61ed2ceSHans Rosenfeld static void
ccid_reply_bulk_cb(usb_pipe_handle_t ph,usb_bulk_req_t * ubrp)1143a61ed2ceSHans Rosenfeld ccid_reply_bulk_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
1144a61ed2ceSHans Rosenfeld {
1145a61ed2ceSHans Rosenfeld 	size_t mlen;
1146a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
1147a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
1148a61ed2ceSHans Rosenfeld 	ccid_header_t cch;
1149a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
1150a61ed2ceSHans Rosenfeld 
1151a61ed2ceSHans Rosenfeld 	boolean_t header_valid = B_FALSE;
1152a61ed2ceSHans Rosenfeld 
1153a61ed2ceSHans Rosenfeld 	VERIFY(ubrp->bulk_data != NULL);
1154a61ed2ceSHans Rosenfeld 	mlen = msgsize(ubrp->bulk_data);
1155a61ed2ceSHans Rosenfeld 	ccid = (ccid_t *)ubrp->bulk_client_private;
1156a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
1157a61ed2ceSHans Rosenfeld 
1158a61ed2ceSHans Rosenfeld 	/*
1159a61ed2ceSHans Rosenfeld 	 * Before we do anything else, we should mark that this Bulk-IN request
1160a61ed2ceSHans Rosenfeld 	 * is no longer being dispatched.
1161a61ed2ceSHans Rosenfeld 	 */
1162a61ed2ceSHans Rosenfeld 	VERIFY3P(ubrp, ==, ccid->ccid_bulkin_dispatched);
1163a61ed2ceSHans Rosenfeld 	ccid->ccid_bulkin_dispatched = NULL;
1164a61ed2ceSHans Rosenfeld 
1165a61ed2ceSHans Rosenfeld 	if ((cc = list_head(&ccid->ccid_command_queue)) == NULL) {
1166a61ed2ceSHans Rosenfeld 		/*
1167a61ed2ceSHans Rosenfeld 		 * This is certainly an odd case. This means that we got some
1168a61ed2ceSHans Rosenfeld 		 * response but there are no entries in the queue. Go ahead and
1169a61ed2ceSHans Rosenfeld 		 * free this. We're done here.
1170a61ed2ceSHans Rosenfeld 		 */
1171a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
1172a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(ubrp);
1173a61ed2ceSHans Rosenfeld 		return;
1174a61ed2ceSHans Rosenfeld 	}
1175a61ed2ceSHans Rosenfeld 
1176a61ed2ceSHans Rosenfeld 	if (mlen >= sizeof (ccid_header_t)) {
1177a61ed2ceSHans Rosenfeld 		bcopy(ubrp->bulk_data->b_rptr, &cch, sizeof (cch));
1178a61ed2ceSHans Rosenfeld 		header_valid = B_TRUE;
1179a61ed2ceSHans Rosenfeld 	}
1180a61ed2ceSHans Rosenfeld 
1181a61ed2ceSHans Rosenfeld 	/*
1182a61ed2ceSHans Rosenfeld 	 * If the current command isn't in the replying state, then something is
1183a61ed2ceSHans Rosenfeld 	 * clearly wrong and this probably isn't intended for the current
1184a61ed2ceSHans Rosenfeld 	 * command. That said, if we have enough bytes, let's check the sequence
1185a61ed2ceSHans Rosenfeld 	 * number as that might be indicative of a bug otherwise.
1186a61ed2ceSHans Rosenfeld 	 */
1187a61ed2ceSHans Rosenfeld 	if (cc->cc_state != CCID_COMMAND_REPLYING) {
1188a61ed2ceSHans Rosenfeld 		if (header_valid) {
1189a61ed2ceSHans Rosenfeld 			VERIFY3S(cch.ch_seq, !=, cc->cc_seq);
1190a61ed2ceSHans Rosenfeld 		}
1191a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
1192a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(ubrp);
1193a61ed2ceSHans Rosenfeld 		return;
1194a61ed2ceSHans Rosenfeld 	}
1195a61ed2ceSHans Rosenfeld 
1196a61ed2ceSHans Rosenfeld 	/*
1197a61ed2ceSHans Rosenfeld 	 * CCID section 6.2.7 says that if we get a short or zero length packet,
1198a61ed2ceSHans Rosenfeld 	 * then we need to treat that as though the running command was aborted
1199a61ed2ceSHans Rosenfeld 	 * for some reason. However, section 3.1.3 talks about sending zero
1200a61ed2ceSHans Rosenfeld 	 * length packets on general principle.  To further complicate things,
1201a61ed2ceSHans Rosenfeld 	 * we don't have the sequence number.
1202a61ed2ceSHans Rosenfeld 	 *
1203a61ed2ceSHans Rosenfeld 	 * If we have an outstanding command still, then we opt to treat the
1204a61ed2ceSHans Rosenfeld 	 * zero length packet as an abort.
1205a61ed2ceSHans Rosenfeld 	 */
1206a61ed2ceSHans Rosenfeld 	if (!header_valid) {
1207a61ed2ceSHans Rosenfeld 		ccid_command_state_transition(cc, CCID_COMMAND_CCID_ABORTED);
1208a61ed2ceSHans Rosenfeld 		ccid_command_complete(cc);
1209a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
1210a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(ubrp);
1211a61ed2ceSHans Rosenfeld 		return;
1212a61ed2ceSHans Rosenfeld 	}
1213a61ed2ceSHans Rosenfeld 
1214a61ed2ceSHans Rosenfeld 	slot = &ccid->ccid_slots[cc->cc_slot];
1215a61ed2ceSHans Rosenfeld 
1216a61ed2ceSHans Rosenfeld 	/*
1217a61ed2ceSHans Rosenfeld 	 * If the sequence or slot number don't match the head of the list or
1218a61ed2ceSHans Rosenfeld 	 * the response type is unexpected for this command then we should be
1219a61ed2ceSHans Rosenfeld 	 * very suspect of the hardware at this point. At a minimum we should
1220a61ed2ceSHans Rosenfeld 	 * fail this command and issue a reset.
1221a61ed2ceSHans Rosenfeld 	 */
1222a61ed2ceSHans Rosenfeld 	if (cch.ch_seq != cc->cc_seq ||
1223a61ed2ceSHans Rosenfeld 	    cch.ch_slot != cc->cc_slot ||
1224a61ed2ceSHans Rosenfeld 	    cch.ch_mtype != cc->cc_rtype) {
1225a61ed2ceSHans Rosenfeld 		ccid_command_state_transition(cc, CCID_COMMAND_CCID_ABORTED);
1226a61ed2ceSHans Rosenfeld 		ccid_command_complete(cc);
1227a61ed2ceSHans Rosenfeld 		slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
1228a61ed2ceSHans Rosenfeld 		ccid_worker_request(ccid);
1229a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
1230a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(ubrp);
1231a61ed2ceSHans Rosenfeld 		return;
1232a61ed2ceSHans Rosenfeld 	}
1233a61ed2ceSHans Rosenfeld 
1234a61ed2ceSHans Rosenfeld 	/*
1235a61ed2ceSHans Rosenfeld 	 * Check that we have all the bytes that we were told we'd have. If we
1236a61ed2ceSHans Rosenfeld 	 * don't, simulate this as an aborted command and issue a reset.
1237a61ed2ceSHans Rosenfeld 	 */
1238a61ed2ceSHans Rosenfeld 	if (LE_32(cch.ch_length) + sizeof (ccid_header_t) > mlen) {
1239a61ed2ceSHans Rosenfeld 		ccid_command_state_transition(cc, CCID_COMMAND_CCID_ABORTED);
1240a61ed2ceSHans Rosenfeld 		ccid_command_complete(cc);
1241a61ed2ceSHans Rosenfeld 		slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
1242a61ed2ceSHans Rosenfeld 		ccid_worker_request(ccid);
1243a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
1244a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(ubrp);
1245a61ed2ceSHans Rosenfeld 		return;
1246a61ed2ceSHans Rosenfeld 	}
1247a61ed2ceSHans Rosenfeld 
1248a61ed2ceSHans Rosenfeld 	/*
1249a61ed2ceSHans Rosenfeld 	 * This response is for us. Before we complete the command check to see
1250a61ed2ceSHans Rosenfeld 	 * what the state of the command is. If the command indicates that more
1251a61ed2ceSHans Rosenfeld 	 * time has been requested, then we need to schedule a new Bulk-IN
1252a61ed2ceSHans Rosenfeld 	 * request.
1253a61ed2ceSHans Rosenfeld 	 */
1254a61ed2ceSHans Rosenfeld 	if (CCID_REPLY_STATUS(cch.ch_param0) == CCID_REPLY_STATUS_MORE_TIME) {
1255a61ed2ceSHans Rosenfeld 		int ret;
1256a61ed2ceSHans Rosenfeld 
1257a61ed2ceSHans Rosenfeld 		ret = ccid_bulkin_schedule(ccid);
1258a61ed2ceSHans Rosenfeld 		if (ret != USB_SUCCESS) {
1259a61ed2ceSHans Rosenfeld 			ccid_command_transport_error(cc, ret, USB_CR_OK);
1260a61ed2ceSHans Rosenfeld 			slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
1261a61ed2ceSHans Rosenfeld 			ccid_worker_request(ccid);
1262a61ed2ceSHans Rosenfeld 		}
1263a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
1264a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(ubrp);
1265a61ed2ceSHans Rosenfeld 		return;
1266a61ed2ceSHans Rosenfeld 	}
1267a61ed2ceSHans Rosenfeld 
1268a61ed2ceSHans Rosenfeld 	/*
1269a61ed2ceSHans Rosenfeld 	 * Take the message block from the Bulk-IN request and store it on the
1270a61ed2ceSHans Rosenfeld 	 * command. We want this regardless if it succeeded, failed, or we have
1271a61ed2ceSHans Rosenfeld 	 * some unexpected status value.
1272a61ed2ceSHans Rosenfeld 	 */
1273a61ed2ceSHans Rosenfeld 	cc->cc_response = ubrp->bulk_data;
1274a61ed2ceSHans Rosenfeld 	ubrp->bulk_data = NULL;
1275a61ed2ceSHans Rosenfeld 	ccid_command_state_transition(cc, CCID_COMMAND_COMPLETE);
1276a61ed2ceSHans Rosenfeld 	ccid_command_complete(cc);
1277a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
1278a61ed2ceSHans Rosenfeld 	usb_free_bulk_req(ubrp);
1279a61ed2ceSHans Rosenfeld }
1280a61ed2ceSHans Rosenfeld 
1281a61ed2ceSHans Rosenfeld static void
ccid_reply_bulk_exc_cb(usb_pipe_handle_t ph,usb_bulk_req_t * ubrp)1282a61ed2ceSHans Rosenfeld ccid_reply_bulk_exc_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
1283a61ed2ceSHans Rosenfeld {
1284a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
1285a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
1286a61ed2ceSHans Rosenfeld 
1287a61ed2ceSHans Rosenfeld 	ccid = (ccid_t *)ubrp->bulk_client_private;
1288a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
1289a61ed2ceSHans Rosenfeld 
1290a61ed2ceSHans Rosenfeld 	/*
1291a61ed2ceSHans Rosenfeld 	 * Before we do anything else, we should mark that this Bulk-IN request
1292a61ed2ceSHans Rosenfeld 	 * is no longer being dispatched.
1293a61ed2ceSHans Rosenfeld 	 */
1294a61ed2ceSHans Rosenfeld 	VERIFY3P(ubrp, ==, ccid->ccid_bulkin_dispatched);
1295a61ed2ceSHans Rosenfeld 	ccid->ccid_bulkin_dispatched = NULL;
1296a61ed2ceSHans Rosenfeld 
1297a61ed2ceSHans Rosenfeld 	/*
1298a61ed2ceSHans Rosenfeld 	 * While there are many different reasons that the Bulk-IN request could
1299a61ed2ceSHans Rosenfeld 	 * have failed, each of these are treated as a transport error. If we
1300a61ed2ceSHans Rosenfeld 	 * have a dispatched command, then we treat this as corresponding to
1301a61ed2ceSHans Rosenfeld 	 * that command. Otherwise, we drop this.
1302a61ed2ceSHans Rosenfeld 	 */
1303a61ed2ceSHans Rosenfeld 	if ((cc = list_head(&ccid->ccid_command_queue)) != NULL) {
1304a61ed2ceSHans Rosenfeld 		if (cc->cc_state == CCID_COMMAND_REPLYING) {
1305a61ed2ceSHans Rosenfeld 			ccid_command_transport_error(cc, USB_SUCCESS,
1306a61ed2ceSHans Rosenfeld 			    ubrp->bulk_completion_reason);
1307a61ed2ceSHans Rosenfeld 		}
1308a61ed2ceSHans Rosenfeld 	}
1309a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
1310a61ed2ceSHans Rosenfeld 	usb_free_bulk_req(ubrp);
1311a61ed2ceSHans Rosenfeld }
1312a61ed2ceSHans Rosenfeld 
1313a61ed2ceSHans Rosenfeld /*
1314a61ed2ceSHans Rosenfeld  * Fill the Bulk-IN cache. If we do not entirely fill this, that's fine. If
1315a61ed2ceSHans Rosenfeld  * there are no scheduled resources then we'll deal with that when we actually
1316a61ed2ceSHans Rosenfeld  * get there.
1317a61ed2ceSHans Rosenfeld  */
1318a61ed2ceSHans Rosenfeld static void
ccid_bulkin_cache_refresh(ccid_t * ccid)1319a61ed2ceSHans Rosenfeld ccid_bulkin_cache_refresh(ccid_t *ccid)
1320a61ed2ceSHans Rosenfeld {
1321a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
1322a61ed2ceSHans Rosenfeld 	while (ccid->ccid_bulkin_alloced < CCID_BULK_NALLOCED) {
1323a61ed2ceSHans Rosenfeld 		usb_bulk_req_t *ubrp;
1324a61ed2ceSHans Rosenfeld 
1325a61ed2ceSHans Rosenfeld 		if ((ubrp = usb_alloc_bulk_req(ccid->ccid_dip,
1326a61ed2ceSHans Rosenfeld 		    ccid->ccid_bufsize, 0)) == NULL)
1327a61ed2ceSHans Rosenfeld 			return;
1328a61ed2ceSHans Rosenfeld 
1329a61ed2ceSHans Rosenfeld 		ubrp->bulk_len = ccid->ccid_bufsize;
1330a61ed2ceSHans Rosenfeld 		ubrp->bulk_timeout = CCID_BULK_IN_TIMEOUT;
1331a61ed2ceSHans Rosenfeld 		ubrp->bulk_client_private = (usb_opaque_t)ccid;
1332a61ed2ceSHans Rosenfeld 		ubrp->bulk_attributes = USB_ATTRS_SHORT_XFER_OK |
1333a61ed2ceSHans Rosenfeld 		    USB_ATTRS_AUTOCLEARING;
1334a61ed2ceSHans Rosenfeld 		ubrp->bulk_cb = ccid_reply_bulk_cb;
1335a61ed2ceSHans Rosenfeld 		ubrp->bulk_exc_cb = ccid_reply_bulk_exc_cb;
1336a61ed2ceSHans Rosenfeld 
1337a61ed2ceSHans Rosenfeld 		ccid->ccid_bulkin_cache[ccid->ccid_bulkin_alloced] = ubrp;
1338a61ed2ceSHans Rosenfeld 		ccid->ccid_bulkin_alloced++;
1339a61ed2ceSHans Rosenfeld 	}
1340a61ed2ceSHans Rosenfeld 
1341a61ed2ceSHans Rosenfeld }
1342a61ed2ceSHans Rosenfeld 
1343a61ed2ceSHans Rosenfeld static usb_bulk_req_t *
ccid_bulkin_cache_get(ccid_t * ccid)1344a61ed2ceSHans Rosenfeld ccid_bulkin_cache_get(ccid_t *ccid)
1345a61ed2ceSHans Rosenfeld {
1346a61ed2ceSHans Rosenfeld 	usb_bulk_req_t *ubrp;
1347a61ed2ceSHans Rosenfeld 
1348a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
1349a61ed2ceSHans Rosenfeld 
1350a61ed2ceSHans Rosenfeld 	if (ccid->ccid_bulkin_alloced == 0) {
1351a61ed2ceSHans Rosenfeld 		ccid_bulkin_cache_refresh(ccid);
1352a61ed2ceSHans Rosenfeld 		if (ccid->ccid_bulkin_alloced == 0)
1353a61ed2ceSHans Rosenfeld 			return (NULL);
1354a61ed2ceSHans Rosenfeld 	}
1355a61ed2ceSHans Rosenfeld 
1356a61ed2ceSHans Rosenfeld 	ccid->ccid_bulkin_alloced--;
1357a61ed2ceSHans Rosenfeld 	ubrp = ccid->ccid_bulkin_cache[ccid->ccid_bulkin_alloced];
1358a61ed2ceSHans Rosenfeld 	VERIFY3P(ubrp, !=, NULL);
1359a61ed2ceSHans Rosenfeld 	ccid->ccid_bulkin_cache[ccid->ccid_bulkin_alloced] = NULL;
1360a61ed2ceSHans Rosenfeld 
1361a61ed2ceSHans Rosenfeld 	return (ubrp);
1362a61ed2ceSHans Rosenfeld }
1363a61ed2ceSHans Rosenfeld 
1364a61ed2ceSHans Rosenfeld /*
1365a61ed2ceSHans Rosenfeld  * Attempt to schedule a Bulk-In request. Note that only one should ever be
1366a61ed2ceSHans Rosenfeld  * scheduled at any time.
1367a61ed2ceSHans Rosenfeld  */
1368a61ed2ceSHans Rosenfeld static int
ccid_bulkin_schedule(ccid_t * ccid)1369a61ed2ceSHans Rosenfeld ccid_bulkin_schedule(ccid_t *ccid)
1370a61ed2ceSHans Rosenfeld {
1371a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
1372a61ed2ceSHans Rosenfeld 	if (ccid->ccid_bulkin_dispatched == NULL) {
1373a61ed2ceSHans Rosenfeld 		usb_bulk_req_t *ubrp;
1374a61ed2ceSHans Rosenfeld 		int ret;
1375a61ed2ceSHans Rosenfeld 
1376a61ed2ceSHans Rosenfeld 		ubrp = ccid_bulkin_cache_get(ccid);
1377a61ed2ceSHans Rosenfeld 		if (ubrp == NULL) {
1378a61ed2ceSHans Rosenfeld 			return (USB_NO_RESOURCES);
1379a61ed2ceSHans Rosenfeld 		}
1380a61ed2ceSHans Rosenfeld 
1381a61ed2ceSHans Rosenfeld 		if ((ret = usb_pipe_bulk_xfer(ccid->ccid_bulkin_pipe, ubrp,
1382a61ed2ceSHans Rosenfeld 		    0)) != USB_SUCCESS) {
1383a61ed2ceSHans Rosenfeld 			ccid_error(ccid,
1384a61ed2ceSHans Rosenfeld 			    "!failed to schedule Bulk-In response: %d", ret);
1385a61ed2ceSHans Rosenfeld 			usb_free_bulk_req(ubrp);
1386a61ed2ceSHans Rosenfeld 			return (ret);
1387a61ed2ceSHans Rosenfeld 		}
1388a61ed2ceSHans Rosenfeld 
1389a61ed2ceSHans Rosenfeld 		ccid->ccid_bulkin_dispatched = ubrp;
1390a61ed2ceSHans Rosenfeld 	}
1391a61ed2ceSHans Rosenfeld 
1392a61ed2ceSHans Rosenfeld 	return (USB_SUCCESS);
1393a61ed2ceSHans Rosenfeld }
1394a61ed2ceSHans Rosenfeld 
1395a61ed2ceSHans Rosenfeld /*
1396a61ed2ceSHans Rosenfeld  * Make sure that the head of the queue has been dispatched. If a dispatch to
1397a61ed2ceSHans Rosenfeld  * the device fails, fail the command and try the next one.
1398a61ed2ceSHans Rosenfeld  */
1399a61ed2ceSHans Rosenfeld static void
ccid_command_dispatch(ccid_t * ccid)1400a61ed2ceSHans Rosenfeld ccid_command_dispatch(ccid_t *ccid)
1401a61ed2ceSHans Rosenfeld {
1402a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
1403a61ed2ceSHans Rosenfeld 
1404a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
1405a61ed2ceSHans Rosenfeld 	while ((cc = list_head(&ccid->ccid_command_queue)) != NULL) {
1406a61ed2ceSHans Rosenfeld 		int ret;
1407a61ed2ceSHans Rosenfeld 
1408a61ed2ceSHans Rosenfeld 		if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0)
1409a61ed2ceSHans Rosenfeld 			return;
1410a61ed2ceSHans Rosenfeld 
1411a61ed2ceSHans Rosenfeld 		/*
1412a61ed2ceSHans Rosenfeld 		 * Head of the queue is already being processed. We're done
1413a61ed2ceSHans Rosenfeld 		 * here.
1414a61ed2ceSHans Rosenfeld 		 */
1415a61ed2ceSHans Rosenfeld 		if (cc->cc_state > CCID_COMMAND_QUEUED) {
1416a61ed2ceSHans Rosenfeld 			return;
1417a61ed2ceSHans Rosenfeld 		}
1418a61ed2ceSHans Rosenfeld 
1419a61ed2ceSHans Rosenfeld 		/*
1420a61ed2ceSHans Rosenfeld 		 * Mark the command as being dispatched to the device. This
1421a61ed2ceSHans Rosenfeld 		 * prevents anyone else from getting in and confusing things.
1422a61ed2ceSHans Rosenfeld 		 */
1423a61ed2ceSHans Rosenfeld 		ccid_command_state_transition(cc, CCID_COMMAND_DISPATCHED);
1424a61ed2ceSHans Rosenfeld 		cc->cc_dispatch_time = gethrtime();
1425a61ed2ceSHans Rosenfeld 
1426a61ed2ceSHans Rosenfeld 		/*
1427a61ed2ceSHans Rosenfeld 		 * Drop the global lock while we schedule the USB I/O.
1428a61ed2ceSHans Rosenfeld 		 */
1429a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
1430a61ed2ceSHans Rosenfeld 
1431a61ed2ceSHans Rosenfeld 		ret = usb_pipe_bulk_xfer(ccid->ccid_bulkout_pipe, cc->cc_ubrp,
1432a61ed2ceSHans Rosenfeld 		    0);
1433a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
1434a61ed2ceSHans Rosenfeld 		if (ret != USB_SUCCESS) {
1435a61ed2ceSHans Rosenfeld 			/*
1436a61ed2ceSHans Rosenfeld 			 * We don't need to free the usb_bulk_req_t here as it
1437a61ed2ceSHans Rosenfeld 			 * will be taken care of when the command itself is
1438a61ed2ceSHans Rosenfeld 			 * freed.
1439a61ed2ceSHans Rosenfeld 			 */
1440a61ed2ceSHans Rosenfeld 			ccid_error(ccid, "!Bulk pipe dispatch failed: %d\n",
1441a61ed2ceSHans Rosenfeld 			    ret);
1442a61ed2ceSHans Rosenfeld 			ccid_command_transport_error(cc, ret, USB_CR_OK);
1443a61ed2ceSHans Rosenfeld 		}
1444a61ed2ceSHans Rosenfeld 	}
1445a61ed2ceSHans Rosenfeld }
1446a61ed2ceSHans Rosenfeld 
1447a61ed2ceSHans Rosenfeld static int
ccid_command_queue(ccid_t * ccid,ccid_command_t * cc)1448a61ed2ceSHans Rosenfeld ccid_command_queue(ccid_t *ccid, ccid_command_t *cc)
1449a61ed2ceSHans Rosenfeld {
1450a61ed2ceSHans Rosenfeld 	id_t seq;
1451a61ed2ceSHans Rosenfeld 	ccid_header_t *cchead;
1452a61ed2ceSHans Rosenfeld 
1453a61ed2ceSHans Rosenfeld 	seq = id_alloc_nosleep(ccid->ccid_seqs);
1454a61ed2ceSHans Rosenfeld 	if (seq == -1)
1455a61ed2ceSHans Rosenfeld 		return (ENOMEM);
1456a61ed2ceSHans Rosenfeld 	cc->cc_seq = seq;
1457a61ed2ceSHans Rosenfeld 	VERIFY3U(seq, <=, UINT8_MAX);
1458a61ed2ceSHans Rosenfeld 	cchead = (void *)cc->cc_ubrp->bulk_data->b_rptr;
1459a61ed2ceSHans Rosenfeld 	cchead->ch_seq = (uint8_t)seq;
1460a61ed2ceSHans Rosenfeld 
1461a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
1462a61ed2ceSHans Rosenfeld 	/*
1463a61ed2ceSHans Rosenfeld 	 * Take a shot at filling up our reply cache while we're submitting this
1464a61ed2ceSHans Rosenfeld 	 * command.
1465a61ed2ceSHans Rosenfeld 	 */
1466a61ed2ceSHans Rosenfeld 	ccid_bulkin_cache_refresh(ccid);
1467a61ed2ceSHans Rosenfeld 	list_insert_tail(&ccid->ccid_command_queue, cc);
1468a61ed2ceSHans Rosenfeld 	ccid_command_state_transition(cc, CCID_COMMAND_QUEUED);
1469a61ed2ceSHans Rosenfeld 	cc->cc_queue_time = gethrtime();
1470a61ed2ceSHans Rosenfeld 	ccid_command_dispatch(ccid);
1471a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
1472a61ed2ceSHans Rosenfeld 
1473a61ed2ceSHans Rosenfeld 	return (0);
1474a61ed2ceSHans Rosenfeld }
1475a61ed2ceSHans Rosenfeld 
1476a61ed2ceSHans Rosenfeld /*
1477a61ed2ceSHans Rosenfeld  * Normal callback for Bulk-Out requests which represents commands issued to the
1478a61ed2ceSHans Rosenfeld  * device.
1479a61ed2ceSHans Rosenfeld  */
1480a61ed2ceSHans Rosenfeld static void
ccid_dispatch_bulk_cb(usb_pipe_handle_t ph,usb_bulk_req_t * ubrp)1481a61ed2ceSHans Rosenfeld ccid_dispatch_bulk_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
1482a61ed2ceSHans Rosenfeld {
1483a61ed2ceSHans Rosenfeld 	int ret;
1484a61ed2ceSHans Rosenfeld 	ccid_command_t *cc = (void *)ubrp->bulk_client_private;
1485a61ed2ceSHans Rosenfeld 	ccid_t *ccid = cc->cc_ccid;
1486a61ed2ceSHans Rosenfeld 
1487a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
1488a61ed2ceSHans Rosenfeld 	VERIFY3S(cc->cc_state, ==, CCID_COMMAND_DISPATCHED);
1489a61ed2ceSHans Rosenfeld 	ccid_command_state_transition(cc, CCID_COMMAND_REPLYING);
1490a61ed2ceSHans Rosenfeld 	cc->cc_dispatch_cb_time = gethrtime();
1491a61ed2ceSHans Rosenfeld 
1492a61ed2ceSHans Rosenfeld 	/*
1493a61ed2ceSHans Rosenfeld 	 * Since we have successfully sent the command, give it a Bulk-In
1494a61ed2ceSHans Rosenfeld 	 * response to reply to us with. If that fails, we'll note a transport
1495a61ed2ceSHans Rosenfeld 	 * error which will kick off the next command if needed.
1496a61ed2ceSHans Rosenfeld 	 */
1497a61ed2ceSHans Rosenfeld 	ret = ccid_bulkin_schedule(ccid);
1498a61ed2ceSHans Rosenfeld 	if (ret != USB_SUCCESS) {
1499a61ed2ceSHans Rosenfeld 		ccid_command_transport_error(cc, ret, USB_CR_OK);
1500a61ed2ceSHans Rosenfeld 	}
1501a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
1502a61ed2ceSHans Rosenfeld }
1503a61ed2ceSHans Rosenfeld 
1504a61ed2ceSHans Rosenfeld /*
1505a61ed2ceSHans Rosenfeld  * Exception callback for the Bulk-Out requests which represent commands issued
1506a61ed2ceSHans Rosenfeld  * to the device.
1507a61ed2ceSHans Rosenfeld  */
1508a61ed2ceSHans Rosenfeld static void
ccid_dispatch_bulk_exc_cb(usb_pipe_handle_t ph,usb_bulk_req_t * ubrp)1509a61ed2ceSHans Rosenfeld ccid_dispatch_bulk_exc_cb(usb_pipe_handle_t ph, usb_bulk_req_t *ubrp)
1510a61ed2ceSHans Rosenfeld {
1511a61ed2ceSHans Rosenfeld 	ccid_command_t *cc = (void *)ubrp->bulk_client_private;
1512a61ed2ceSHans Rosenfeld 	ccid_t *ccid = cc->cc_ccid;
1513a61ed2ceSHans Rosenfeld 
1514a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
1515a61ed2ceSHans Rosenfeld 	ccid_command_transport_error(cc, USB_SUCCESS,
1516a61ed2ceSHans Rosenfeld 	    ubrp->bulk_completion_reason);
1517a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
1518a61ed2ceSHans Rosenfeld }
1519a61ed2ceSHans Rosenfeld 
1520a61ed2ceSHans Rosenfeld static void
ccid_command_free(ccid_command_t * cc)1521a61ed2ceSHans Rosenfeld ccid_command_free(ccid_command_t *cc)
1522a61ed2ceSHans Rosenfeld {
1523a61ed2ceSHans Rosenfeld 	VERIFY0(list_link_active(&cc->cc_list_node));
1524a61ed2ceSHans Rosenfeld 	VERIFY(cc->cc_state == CCID_COMMAND_ALLOCATED ||
1525a61ed2ceSHans Rosenfeld 	    cc->cc_state >= CCID_COMMAND_COMPLETE);
1526a61ed2ceSHans Rosenfeld 
1527a61ed2ceSHans Rosenfeld 	if (cc->cc_response != NULL) {
1528a61ed2ceSHans Rosenfeld 		freemsgchain(cc->cc_response);
1529a61ed2ceSHans Rosenfeld 		cc->cc_response = NULL;
1530a61ed2ceSHans Rosenfeld 	}
1531a61ed2ceSHans Rosenfeld 
1532a61ed2ceSHans Rosenfeld 	if (cc->cc_ubrp != NULL) {
1533a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(cc->cc_ubrp);
1534a61ed2ceSHans Rosenfeld 		cc->cc_ubrp = NULL;
1535a61ed2ceSHans Rosenfeld 	}
1536a61ed2ceSHans Rosenfeld 
1537a61ed2ceSHans Rosenfeld 	if (cc->cc_seq != 0) {
1538a61ed2ceSHans Rosenfeld 		id_free(cc->cc_ccid->ccid_seqs, cc->cc_seq);
1539a61ed2ceSHans Rosenfeld 		cc->cc_seq = 0;
1540a61ed2ceSHans Rosenfeld 	}
1541a61ed2ceSHans Rosenfeld 
1542a61ed2ceSHans Rosenfeld 	cv_destroy(&cc->cc_cv);
1543a61ed2ceSHans Rosenfeld 	kmem_free(cc, sizeof (ccid_command_t));
1544a61ed2ceSHans Rosenfeld }
1545a61ed2ceSHans Rosenfeld 
1546a61ed2ceSHans Rosenfeld /*
1547a61ed2ceSHans Rosenfeld  * Copy len bytes of data from buf into the allocated message block.
1548a61ed2ceSHans Rosenfeld  */
1549a61ed2ceSHans Rosenfeld static void
ccid_command_bcopy(ccid_command_t * cc,const void * buf,size_t len)1550a61ed2ceSHans Rosenfeld ccid_command_bcopy(ccid_command_t *cc, const void *buf, size_t len)
1551a61ed2ceSHans Rosenfeld {
1552a61ed2ceSHans Rosenfeld 	size_t mlen;
1553a61ed2ceSHans Rosenfeld 
1554a61ed2ceSHans Rosenfeld 	mlen = msgsize(cc->cc_ubrp->bulk_data);
1555a61ed2ceSHans Rosenfeld 	VERIFY3U(mlen + len, >=, len);
1556a61ed2ceSHans Rosenfeld 	VERIFY3U(mlen + len, >=, mlen);
1557a61ed2ceSHans Rosenfeld 	mlen += len;
1558a61ed2ceSHans Rosenfeld 	VERIFY3U(mlen, <=, cc->cc_ubrp->bulk_len);
1559a61ed2ceSHans Rosenfeld 
1560a61ed2ceSHans Rosenfeld 	bcopy(buf, cc->cc_ubrp->bulk_data->b_wptr, len);
1561a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_data->b_wptr += len;
1562a61ed2ceSHans Rosenfeld }
1563a61ed2ceSHans Rosenfeld 
1564a61ed2ceSHans Rosenfeld /*
1565a61ed2ceSHans Rosenfeld  * Allocate a command of a specific size and parameters. This will allocate a
1566a61ed2ceSHans Rosenfeld  * USB bulk transfer that the caller will copy data to.
1567a61ed2ceSHans Rosenfeld  */
1568a61ed2ceSHans Rosenfeld static int
ccid_command_alloc(ccid_t * ccid,ccid_slot_t * slot,boolean_t block,mblk_t * datamp,size_t datasz,uint8_t mtype,uint8_t param0,uint8_t param1,uint8_t param2,ccid_command_t ** ccp)1569a61ed2ceSHans Rosenfeld ccid_command_alloc(ccid_t *ccid, ccid_slot_t *slot, boolean_t block,
1570a61ed2ceSHans Rosenfeld     mblk_t *datamp, size_t datasz, uint8_t mtype, uint8_t param0,
1571a61ed2ceSHans Rosenfeld     uint8_t param1, uint8_t param2, ccid_command_t **ccp)
1572a61ed2ceSHans Rosenfeld {
1573a61ed2ceSHans Rosenfeld 	size_t allocsz;
1574a61ed2ceSHans Rosenfeld 	int kmflag, usbflag;
1575a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
1576a61ed2ceSHans Rosenfeld 	ccid_header_t *cchead;
1577a61ed2ceSHans Rosenfeld 	ccid_response_code_t rtype;
1578a61ed2ceSHans Rosenfeld 
1579a61ed2ceSHans Rosenfeld 	switch (mtype) {
1580a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_POWER_ON:
1581a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_POWER_OFF:
1582a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_SLOT_STATUS:
1583a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_GET_PARAMS:
1584a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_RESET_PARAMS:
1585a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_ICC_CLOCK:
1586a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_T0APDU:
1587a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_MECHANICAL:
1588a61ed2ceSHans Rosenfeld 	case CCID_REQEUST_ABORT:
1589a61ed2ceSHans Rosenfeld 		if (datasz != 0)
1590a61ed2ceSHans Rosenfeld 			return (EINVAL);
1591a61ed2ceSHans Rosenfeld 		break;
1592a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_TRANSFER_BLOCK:
1593a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_ESCAPE:
1594a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_SECURE:
1595a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_SET_PARAMS:
1596a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_DATA_CLOCK:
1597a61ed2ceSHans Rosenfeld 		break;
1598a61ed2ceSHans Rosenfeld 	default:
1599a61ed2ceSHans Rosenfeld 		return (EINVAL);
1600a61ed2ceSHans Rosenfeld 	}
1601a61ed2ceSHans Rosenfeld 
1602a61ed2ceSHans Rosenfeld 	switch (mtype) {
1603a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_POWER_ON:
1604a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_SECURE:
1605a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_TRANSFER_BLOCK:
1606a61ed2ceSHans Rosenfeld 		rtype = CCID_RESPONSE_DATA_BLOCK;
1607a61ed2ceSHans Rosenfeld 		break;
1608a61ed2ceSHans Rosenfeld 
1609a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_POWER_OFF:
1610a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_SLOT_STATUS:
1611a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_ICC_CLOCK:
1612a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_T0APDU:
1613a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_MECHANICAL:
1614a61ed2ceSHans Rosenfeld 	case CCID_REQEUST_ABORT:
1615a61ed2ceSHans Rosenfeld 		rtype = CCID_RESPONSE_SLOT_STATUS;
1616a61ed2ceSHans Rosenfeld 		break;
1617a61ed2ceSHans Rosenfeld 
1618a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_GET_PARAMS:
1619a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_RESET_PARAMS:
1620a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_SET_PARAMS:
1621a61ed2ceSHans Rosenfeld 		rtype = CCID_RESPONSE_PARAMETERS;
1622a61ed2ceSHans Rosenfeld 		break;
1623a61ed2ceSHans Rosenfeld 
1624a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_ESCAPE:
1625a61ed2ceSHans Rosenfeld 		rtype = CCID_RESPONSE_ESCAPE;
1626a61ed2ceSHans Rosenfeld 		break;
1627a61ed2ceSHans Rosenfeld 
1628a61ed2ceSHans Rosenfeld 	case CCID_REQUEST_DATA_CLOCK:
1629a61ed2ceSHans Rosenfeld 		rtype = CCID_RESPONSE_DATA_CLOCK;
1630a61ed2ceSHans Rosenfeld 		break;
1631a61ed2ceSHans Rosenfeld 	default:
1632a61ed2ceSHans Rosenfeld 		return (EINVAL);
1633a61ed2ceSHans Rosenfeld 	}
1634a61ed2ceSHans Rosenfeld 
1635a61ed2ceSHans Rosenfeld 	if (block) {
1636a61ed2ceSHans Rosenfeld 		kmflag = KM_SLEEP;
1637a61ed2ceSHans Rosenfeld 		usbflag = USB_FLAGS_SLEEP;
1638a61ed2ceSHans Rosenfeld 	} else {
1639*ca783257SDan McDonald 		kmflag = KM_NOSLEEP_LAZY;
1640a61ed2ceSHans Rosenfeld 		usbflag = 0;
1641a61ed2ceSHans Rosenfeld 	}
1642a61ed2ceSHans Rosenfeld 
1643a61ed2ceSHans Rosenfeld 	if (datasz + sizeof (ccid_header_t) < datasz)
1644a61ed2ceSHans Rosenfeld 		return (EINVAL);
1645a61ed2ceSHans Rosenfeld 	if (datasz + sizeof (ccid_header_t) > ccid->ccid_bufsize)
1646a61ed2ceSHans Rosenfeld 		return (EINVAL);
1647a61ed2ceSHans Rosenfeld 
1648a61ed2ceSHans Rosenfeld 	cc = kmem_zalloc(sizeof (ccid_command_t), kmflag);
1649a61ed2ceSHans Rosenfeld 	if (cc == NULL)
1650a61ed2ceSHans Rosenfeld 		return (ENOMEM);
1651a61ed2ceSHans Rosenfeld 
1652a61ed2ceSHans Rosenfeld 	allocsz = datasz + sizeof (ccid_header_t);
1653a61ed2ceSHans Rosenfeld 	if (datamp == NULL) {
1654a61ed2ceSHans Rosenfeld 		cc->cc_ubrp = usb_alloc_bulk_req(ccid->ccid_dip, allocsz,
1655a61ed2ceSHans Rosenfeld 		    usbflag);
1656a61ed2ceSHans Rosenfeld 	} else {
1657a61ed2ceSHans Rosenfeld 		cc->cc_ubrp = usb_alloc_bulk_req(ccid->ccid_dip, 0, usbflag);
1658a61ed2ceSHans Rosenfeld 	}
1659a61ed2ceSHans Rosenfeld 	if (cc->cc_ubrp == NULL) {
1660a61ed2ceSHans Rosenfeld 		kmem_free(cc, sizeof (ccid_command_t));
1661a61ed2ceSHans Rosenfeld 		return (ENOMEM);
1662a61ed2ceSHans Rosenfeld 	}
1663a61ed2ceSHans Rosenfeld 
1664a61ed2ceSHans Rosenfeld 	list_link_init(&cc->cc_list_node);
1665a61ed2ceSHans Rosenfeld 	cv_init(&cc->cc_cv, NULL, CV_DRIVER, NULL);
1666a61ed2ceSHans Rosenfeld 	cc->cc_mtype = mtype;
1667a61ed2ceSHans Rosenfeld 	cc->cc_rtype = rtype;
1668a61ed2ceSHans Rosenfeld 	cc->cc_slot = slot->cs_slotno;
1669a61ed2ceSHans Rosenfeld 	cc->cc_reqlen = datasz;
1670a61ed2ceSHans Rosenfeld 	cc->cc_ccid = ccid;
1671a61ed2ceSHans Rosenfeld 	cc->cc_state = CCID_COMMAND_ALLOCATED;
1672a61ed2ceSHans Rosenfeld 
1673a61ed2ceSHans Rosenfeld 	/*
1674a61ed2ceSHans Rosenfeld 	 * Fill in bulk request attributes. Note that short transfers out
1675a61ed2ceSHans Rosenfeld 	 * are not OK.
1676a61ed2ceSHans Rosenfeld 	 */
1677a61ed2ceSHans Rosenfeld 	if (datamp != NULL) {
1678a61ed2ceSHans Rosenfeld 		cc->cc_ubrp->bulk_data = datamp;
1679a61ed2ceSHans Rosenfeld 	}
1680a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_len = allocsz;
1681a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_timeout = CCID_BULK_OUT_TIMEOUT;
1682a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_client_private = (usb_opaque_t)cc;
1683a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_attributes = USB_ATTRS_AUTOCLEARING;
1684a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_cb = ccid_dispatch_bulk_cb;
1685a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_exc_cb = ccid_dispatch_bulk_exc_cb;
1686a61ed2ceSHans Rosenfeld 
1687a61ed2ceSHans Rosenfeld 	/*
1688a61ed2ceSHans Rosenfeld 	 * Fill in the command header. We fill in everything except the sequence
1689a61ed2ceSHans Rosenfeld 	 * number, which is done by the actual dispatch code.
1690a61ed2ceSHans Rosenfeld 	 */
1691a61ed2ceSHans Rosenfeld 	cchead = (void *)cc->cc_ubrp->bulk_data->b_rptr;
1692a61ed2ceSHans Rosenfeld 	cchead->ch_mtype = mtype;
1693a61ed2ceSHans Rosenfeld 	cchead->ch_length = LE_32(datasz);
1694a61ed2ceSHans Rosenfeld 	cchead->ch_slot = slot->cs_slotno;
1695a61ed2ceSHans Rosenfeld 	cchead->ch_seq = 0;
1696a61ed2ceSHans Rosenfeld 	cchead->ch_param0 = param0;
1697a61ed2ceSHans Rosenfeld 	cchead->ch_param1 = param1;
1698a61ed2ceSHans Rosenfeld 	cchead->ch_param2 = param2;
1699a61ed2ceSHans Rosenfeld 	cc->cc_ubrp->bulk_data->b_wptr += sizeof (ccid_header_t);
1700a61ed2ceSHans Rosenfeld 	*ccp = cc;
1701a61ed2ceSHans Rosenfeld 
1702a61ed2ceSHans Rosenfeld 	return (0);
1703a61ed2ceSHans Rosenfeld }
1704a61ed2ceSHans Rosenfeld 
1705a61ed2ceSHans Rosenfeld /*
1706a61ed2ceSHans Rosenfeld  * The rest of the stack is in charge of timing out commands and potentially
1707a61ed2ceSHans Rosenfeld  * aborting them. At this point in time, there's no specific timeout aspect
1708a61ed2ceSHans Rosenfeld  * here.
1709a61ed2ceSHans Rosenfeld  */
1710a61ed2ceSHans Rosenfeld static void
ccid_command_poll(ccid_t * ccid,ccid_command_t * cc)1711a61ed2ceSHans Rosenfeld ccid_command_poll(ccid_t *ccid, ccid_command_t *cc)
1712a61ed2ceSHans Rosenfeld {
1713a61ed2ceSHans Rosenfeld 	VERIFY0(cc->cc_flags & CCID_COMMAND_F_USER);
1714a61ed2ceSHans Rosenfeld 
1715a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
1716a61ed2ceSHans Rosenfeld 	while ((cc->cc_state < CCID_COMMAND_COMPLETE) &&
1717a61ed2ceSHans Rosenfeld 	    (ccid->ccid_flags & CCID_F_DEV_GONE_MASK) == 0) {
1718a61ed2ceSHans Rosenfeld 		cv_wait(&cc->cc_cv, &ccid->ccid_mutex);
1719a61ed2ceSHans Rosenfeld 	}
1720a61ed2ceSHans Rosenfeld 
1721a61ed2ceSHans Rosenfeld 	/*
1722a61ed2ceSHans Rosenfeld 	 * Treat this as a consumption and remove it from the completion list.
1723a61ed2ceSHans Rosenfeld 	 */
1724a61ed2ceSHans Rosenfeld #ifdef DEBUG
1725a61ed2ceSHans Rosenfeld 	ccid_command_t *check;
1726a61ed2ceSHans Rosenfeld 	for (check = list_head(&ccid->ccid_complete_queue); check != NULL;
1727a61ed2ceSHans Rosenfeld 	    check = list_next(&ccid->ccid_complete_queue, check)) {
1728a61ed2ceSHans Rosenfeld 		if (cc == check)
1729a61ed2ceSHans Rosenfeld 			break;
1730a61ed2ceSHans Rosenfeld 	}
1731a61ed2ceSHans Rosenfeld 	ASSERT3P(check, !=, NULL);
1732a61ed2ceSHans Rosenfeld #endif
1733a61ed2ceSHans Rosenfeld 	VERIFY(list_link_active(&cc->cc_list_node));
1734a61ed2ceSHans Rosenfeld 	list_remove(&ccid->ccid_complete_queue, cc);
1735a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
1736a61ed2ceSHans Rosenfeld }
1737a61ed2ceSHans Rosenfeld 
1738a61ed2ceSHans Rosenfeld static int
ccid_command_power_off(ccid_t * ccid,ccid_slot_t * cs)1739a61ed2ceSHans Rosenfeld ccid_command_power_off(ccid_t *ccid, ccid_slot_t *cs)
1740a61ed2ceSHans Rosenfeld {
1741a61ed2ceSHans Rosenfeld 	int ret;
1742a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
1743a61ed2ceSHans Rosenfeld 	ccid_reply_icc_status_t cis;
1744a61ed2ceSHans Rosenfeld 	ccid_reply_command_status_t crs;
1745a61ed2ceSHans Rosenfeld 
1746a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_alloc(ccid, cs, B_TRUE, NULL, 0,
1747a61ed2ceSHans Rosenfeld 	    CCID_REQUEST_POWER_OFF, 0, 0, 0, &cc)) != 0) {
1748a61ed2ceSHans Rosenfeld 		return (ret);
1749a61ed2ceSHans Rosenfeld 	}
1750a61ed2ceSHans Rosenfeld 
1751a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_queue(ccid, cc)) != 0) {
1752a61ed2ceSHans Rosenfeld 		ccid_command_free(cc);
1753a61ed2ceSHans Rosenfeld 		return (ret);
1754a61ed2ceSHans Rosenfeld 	}
1755a61ed2ceSHans Rosenfeld 
1756a61ed2ceSHans Rosenfeld 	ccid_command_poll(ccid, cc);
1757a61ed2ceSHans Rosenfeld 
1758a61ed2ceSHans Rosenfeld 	if (cc->cc_state != CCID_COMMAND_COMPLETE) {
1759a61ed2ceSHans Rosenfeld 		ret = EIO;
1760a61ed2ceSHans Rosenfeld 		goto done;
1761a61ed2ceSHans Rosenfeld 	}
1762a61ed2ceSHans Rosenfeld 
1763a61ed2ceSHans Rosenfeld 	ccid_command_status_decode(cc, &crs, &cis, NULL);
1764a61ed2ceSHans Rosenfeld 	if (crs == CCID_REPLY_STATUS_FAILED) {
1765a61ed2ceSHans Rosenfeld 		if (cis == CCID_REPLY_ICC_MISSING) {
1766a61ed2ceSHans Rosenfeld 			ret = ENXIO;
1767a61ed2ceSHans Rosenfeld 		} else {
1768a61ed2ceSHans Rosenfeld 			ret = EIO;
1769a61ed2ceSHans Rosenfeld 		}
1770a61ed2ceSHans Rosenfeld 	} else {
1771a61ed2ceSHans Rosenfeld 		ret = 0;
1772a61ed2ceSHans Rosenfeld 	}
1773a61ed2ceSHans Rosenfeld done:
1774a61ed2ceSHans Rosenfeld 	ccid_command_free(cc);
1775a61ed2ceSHans Rosenfeld 	return (ret);
1776a61ed2ceSHans Rosenfeld }
1777a61ed2ceSHans Rosenfeld 
1778a61ed2ceSHans Rosenfeld static int
ccid_command_power_on(ccid_t * ccid,ccid_slot_t * cs,ccid_class_voltage_t volt,mblk_t ** atrp)1779a61ed2ceSHans Rosenfeld ccid_command_power_on(ccid_t *ccid, ccid_slot_t *cs, ccid_class_voltage_t volt,
1780a61ed2ceSHans Rosenfeld     mblk_t **atrp)
1781a61ed2ceSHans Rosenfeld {
1782a61ed2ceSHans Rosenfeld 	int ret;
1783a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
1784a61ed2ceSHans Rosenfeld 	ccid_reply_command_status_t crs;
1785a61ed2ceSHans Rosenfeld 	ccid_reply_icc_status_t cis;
1786a61ed2ceSHans Rosenfeld 	ccid_command_err_t cce;
1787a61ed2ceSHans Rosenfeld 
1788a61ed2ceSHans Rosenfeld 	if (atrp == NULL)
1789a61ed2ceSHans Rosenfeld 		return (EINVAL);
1790a61ed2ceSHans Rosenfeld 
1791a61ed2ceSHans Rosenfeld 	*atrp = NULL;
1792a61ed2ceSHans Rosenfeld 
1793a61ed2ceSHans Rosenfeld 	switch (volt) {
1794a61ed2ceSHans Rosenfeld 	case CCID_CLASS_VOLT_AUTO:
1795a61ed2ceSHans Rosenfeld 	case CCID_CLASS_VOLT_5_0:
1796a61ed2ceSHans Rosenfeld 	case CCID_CLASS_VOLT_3_0:
1797a61ed2ceSHans Rosenfeld 	case CCID_CLASS_VOLT_1_8:
1798a61ed2ceSHans Rosenfeld 		break;
1799a61ed2ceSHans Rosenfeld 	default:
1800a61ed2ceSHans Rosenfeld 		return (EINVAL);
1801a61ed2ceSHans Rosenfeld 	}
1802a61ed2ceSHans Rosenfeld 
1803a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_alloc(ccid, cs, B_TRUE, NULL, 0,
1804a61ed2ceSHans Rosenfeld 	    CCID_REQUEST_POWER_ON, volt, 0, 0, &cc)) != 0) {
1805a61ed2ceSHans Rosenfeld 		return (ret);
1806a61ed2ceSHans Rosenfeld 	}
1807a61ed2ceSHans Rosenfeld 
1808a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_queue(ccid, cc)) != 0) {
1809a61ed2ceSHans Rosenfeld 		ccid_command_free(cc);
1810a61ed2ceSHans Rosenfeld 		return (ret);
1811a61ed2ceSHans Rosenfeld 	}
1812a61ed2ceSHans Rosenfeld 
1813a61ed2ceSHans Rosenfeld 	ccid_command_poll(ccid, cc);
1814a61ed2ceSHans Rosenfeld 
1815a61ed2ceSHans Rosenfeld 	if (cc->cc_state != CCID_COMMAND_COMPLETE) {
1816a61ed2ceSHans Rosenfeld 		ret = EIO;
1817a61ed2ceSHans Rosenfeld 		goto done;
1818a61ed2ceSHans Rosenfeld 	}
1819a61ed2ceSHans Rosenfeld 
1820a61ed2ceSHans Rosenfeld 	/*
1821a61ed2ceSHans Rosenfeld 	 * Look for a few specific errors here:
1822a61ed2ceSHans Rosenfeld 	 *
1823a61ed2ceSHans Rosenfeld 	 * - ICC_MUTE via a few potential ways
1824a61ed2ceSHans Rosenfeld 	 * - Bad voltage
1825a61ed2ceSHans Rosenfeld 	 */
1826a61ed2ceSHans Rosenfeld 	ccid_command_status_decode(cc, &crs, &cis, &cce);
1827a61ed2ceSHans Rosenfeld 	if (crs == CCID_REPLY_STATUS_FAILED) {
1828a61ed2ceSHans Rosenfeld 		if (cis == CCID_REPLY_ICC_MISSING) {
1829a61ed2ceSHans Rosenfeld 			ret = ENXIO;
1830a61ed2ceSHans Rosenfeld 		} else if (cis == CCID_REPLY_ICC_INACTIVE &&
1831a61ed2ceSHans Rosenfeld 		    cce == 7) {
1832a61ed2ceSHans Rosenfeld 			/*
1833a61ed2ceSHans Rosenfeld 			 * This means that byte 7 was invalid. In other words,
1834a61ed2ceSHans Rosenfeld 			 * that the voltage wasn't correct. See Table 6.1-2
1835a61ed2ceSHans Rosenfeld 			 * 'Errors' in the CCID r1.1.0 spec.
1836a61ed2ceSHans Rosenfeld 			 */
1837a61ed2ceSHans Rosenfeld 			ret = ENOTSUP;
1838a61ed2ceSHans Rosenfeld 		} else {
1839a61ed2ceSHans Rosenfeld 			ret = EIO;
1840a61ed2ceSHans Rosenfeld 		}
1841a61ed2ceSHans Rosenfeld 	} else {
1842a61ed2ceSHans Rosenfeld 		size_t len;
1843a61ed2ceSHans Rosenfeld 
1844a61ed2ceSHans Rosenfeld 		len = ccid_command_resp_length(cc);
1845a61ed2ceSHans Rosenfeld 		if (len == 0) {
1846a61ed2ceSHans Rosenfeld 			ret = EINVAL;
1847a61ed2ceSHans Rosenfeld 			goto done;
1848a61ed2ceSHans Rosenfeld 		}
1849a61ed2ceSHans Rosenfeld 
1850a61ed2ceSHans Rosenfeld #ifdef	DEBUG
1851a61ed2ceSHans Rosenfeld 		/*
1852a61ed2ceSHans Rosenfeld 		 * This should have already been checked by the response
1853a61ed2ceSHans Rosenfeld 		 * framework, but sanity check this again.
1854a61ed2ceSHans Rosenfeld 		 */
1855a61ed2ceSHans Rosenfeld 		size_t mlen = msgsize(cc->cc_response);
1856a61ed2ceSHans Rosenfeld 		VERIFY3U(mlen, >=, len + sizeof (ccid_header_t));
1857a61ed2ceSHans Rosenfeld #endif
1858a61ed2ceSHans Rosenfeld 
1859a61ed2ceSHans Rosenfeld 		/*
1860a61ed2ceSHans Rosenfeld 		 * Munge the message block to have the ATR. We want to make sure
1861a61ed2ceSHans Rosenfeld 		 * that the write pointer is set to the maximum length that we
1862a61ed2ceSHans Rosenfeld 		 * got back from the driver (the message block could strictly
1863a61ed2ceSHans Rosenfeld 		 * speaking be larger, because we got a larger transfer for some
1864a61ed2ceSHans Rosenfeld 		 * reason).
1865a61ed2ceSHans Rosenfeld 		 */
1866a61ed2ceSHans Rosenfeld 		cc->cc_response->b_rptr += sizeof (ccid_header_t);
1867a61ed2ceSHans Rosenfeld 		cc->cc_response->b_wptr = cc->cc_response->b_rptr + len;
1868a61ed2ceSHans Rosenfeld 		*atrp = cc->cc_response;
1869a61ed2ceSHans Rosenfeld 		cc->cc_response = NULL;
1870a61ed2ceSHans Rosenfeld 		ret = 0;
1871a61ed2ceSHans Rosenfeld 	}
1872a61ed2ceSHans Rosenfeld 
1873a61ed2ceSHans Rosenfeld done:
1874a61ed2ceSHans Rosenfeld 	ccid_command_free(cc);
1875a61ed2ceSHans Rosenfeld 	return (ret);
1876a61ed2ceSHans Rosenfeld }
1877a61ed2ceSHans Rosenfeld 
1878a61ed2ceSHans Rosenfeld static int
ccid_command_get_parameters(ccid_t * ccid,ccid_slot_t * slot,atr_protocol_t * protp,ccid_params_t * paramsp)1879a61ed2ceSHans Rosenfeld ccid_command_get_parameters(ccid_t *ccid, ccid_slot_t *slot,
1880a61ed2ceSHans Rosenfeld     atr_protocol_t *protp, ccid_params_t *paramsp)
1881a61ed2ceSHans Rosenfeld {
1882a61ed2ceSHans Rosenfeld 	int ret;
1883a61ed2ceSHans Rosenfeld 	uint8_t prot;
1884a61ed2ceSHans Rosenfeld 	size_t mlen;
1885a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
1886a61ed2ceSHans Rosenfeld 	ccid_reply_command_status_t crs;
1887a61ed2ceSHans Rosenfeld 	ccid_reply_icc_status_t cis;
1888a61ed2ceSHans Rosenfeld 	const void *cpbuf;
1889a61ed2ceSHans Rosenfeld 
1890a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_alloc(ccid, slot, B_TRUE, NULL, 0,
1891a61ed2ceSHans Rosenfeld 	    CCID_REQUEST_GET_PARAMS, 0, 0, 0, &cc)) != 0) {
1892a61ed2ceSHans Rosenfeld 		return (ret);
1893a61ed2ceSHans Rosenfeld 	}
1894a61ed2ceSHans Rosenfeld 
1895a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_queue(ccid, cc)) != 0)
1896a61ed2ceSHans Rosenfeld 		goto done;
1897a61ed2ceSHans Rosenfeld 
1898a61ed2ceSHans Rosenfeld 	ccid_command_poll(ccid, cc);
1899a61ed2ceSHans Rosenfeld 
1900a61ed2ceSHans Rosenfeld 	if (cc->cc_state != CCID_COMMAND_COMPLETE) {
1901a61ed2ceSHans Rosenfeld 		ret = EIO;
1902a61ed2ceSHans Rosenfeld 		goto done;
1903a61ed2ceSHans Rosenfeld 	}
1904a61ed2ceSHans Rosenfeld 
1905a61ed2ceSHans Rosenfeld 	ccid_command_status_decode(cc, &crs, &cis, NULL);
1906a61ed2ceSHans Rosenfeld 	if (crs != CCID_REPLY_STATUS_COMPLETE) {
1907a61ed2ceSHans Rosenfeld 		if (cis == CCID_REPLY_ICC_MISSING) {
1908a61ed2ceSHans Rosenfeld 			ret = ENXIO;
1909a61ed2ceSHans Rosenfeld 		} else {
1910a61ed2ceSHans Rosenfeld 			ret = EIO;
1911a61ed2ceSHans Rosenfeld 		}
1912a61ed2ceSHans Rosenfeld 		goto done;
1913a61ed2ceSHans Rosenfeld 	}
1914a61ed2ceSHans Rosenfeld 
1915a61ed2ceSHans Rosenfeld 	/*
1916a61ed2ceSHans Rosenfeld 	 * The protocol is in ch_param2 of the header.
1917a61ed2ceSHans Rosenfeld 	 */
1918a61ed2ceSHans Rosenfeld 	prot = ccid_command_resp_param2(cc);
1919a61ed2ceSHans Rosenfeld 	mlen = ccid_command_resp_length(cc);
1920a61ed2ceSHans Rosenfeld 	cpbuf = cc->cc_response->b_rptr + sizeof (ccid_header_t);
1921a61ed2ceSHans Rosenfeld 
1922a61ed2ceSHans Rosenfeld 	ret = 0;
1923a61ed2ceSHans Rosenfeld 	switch (prot) {
1924a61ed2ceSHans Rosenfeld 	case 0:
1925a61ed2ceSHans Rosenfeld 		if (mlen < sizeof (ccid_params_t0_t)) {
1926a61ed2ceSHans Rosenfeld 			ret = EOVERFLOW;
1927a61ed2ceSHans Rosenfeld 			goto done;
1928a61ed2ceSHans Rosenfeld 		}
1929a61ed2ceSHans Rosenfeld 		*protp = ATR_P_T0;
1930a61ed2ceSHans Rosenfeld 		bcopy(cpbuf, &paramsp->ccp_t0, sizeof (ccid_params_t0_t));
1931a61ed2ceSHans Rosenfeld 		break;
1932a61ed2ceSHans Rosenfeld 	case 1:
1933a61ed2ceSHans Rosenfeld 		if (mlen < sizeof (ccid_params_t1_t)) {
1934a61ed2ceSHans Rosenfeld 			ret = EOVERFLOW;
1935a61ed2ceSHans Rosenfeld 			goto done;
1936a61ed2ceSHans Rosenfeld 		}
1937a61ed2ceSHans Rosenfeld 		*protp = ATR_P_T1;
1938a61ed2ceSHans Rosenfeld 		bcopy(cpbuf, &paramsp->ccp_t1, sizeof (ccid_params_t1_t));
1939a61ed2ceSHans Rosenfeld 		break;
1940a61ed2ceSHans Rosenfeld 	default:
1941a61ed2ceSHans Rosenfeld 		ret = ECHRNG;
1942a61ed2ceSHans Rosenfeld 		break;
1943a61ed2ceSHans Rosenfeld 	}
1944a61ed2ceSHans Rosenfeld 
1945a61ed2ceSHans Rosenfeld done:
1946a61ed2ceSHans Rosenfeld 	ccid_command_free(cc);
1947a61ed2ceSHans Rosenfeld 	return (ret);
1948a61ed2ceSHans Rosenfeld }
1949a61ed2ceSHans Rosenfeld 
1950a61ed2ceSHans Rosenfeld static void
ccid_hw_error(ccid_t * ccid,ccid_intr_hwerr_t * hwerr)1951a61ed2ceSHans Rosenfeld ccid_hw_error(ccid_t *ccid, ccid_intr_hwerr_t *hwerr)
1952a61ed2ceSHans Rosenfeld {
1953a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
1954a61ed2ceSHans Rosenfeld 
1955a61ed2ceSHans Rosenfeld 	/* Make sure the slot number is within range. */
1956a61ed2ceSHans Rosenfeld 	if (hwerr->cih_slot >= ccid->ccid_nslots) {
1957a61ed2ceSHans Rosenfeld 		ccid->ccid_stats.cst_intr_inval++;
1958a61ed2ceSHans Rosenfeld 		return;
1959a61ed2ceSHans Rosenfeld 	}
1960a61ed2ceSHans Rosenfeld 
1961a61ed2ceSHans Rosenfeld 	slot = &ccid->ccid_slots[hwerr->cih_slot];
1962a61ed2ceSHans Rosenfeld 
1963a61ed2ceSHans Rosenfeld 	/* The only error condition defined by the spec is overcurrent. */
1964a61ed2ceSHans Rosenfeld 	if (hwerr->cih_code != CCID_INTR_HWERR_OVERCURRENT) {
1965a61ed2ceSHans Rosenfeld 		ccid->ccid_stats.cst_intr_inval++;
1966a61ed2ceSHans Rosenfeld 		return;
1967a61ed2ceSHans Rosenfeld 	}
1968a61ed2ceSHans Rosenfeld 
1969a61ed2ceSHans Rosenfeld 	/*
1970a61ed2ceSHans Rosenfeld 	 * The worker thread will take care of this situation.
1971a61ed2ceSHans Rosenfeld 	 */
1972a61ed2ceSHans Rosenfeld 	slot->cs_flags |= CCID_SLOT_F_INTR_OVERCURRENT;
1973a61ed2ceSHans Rosenfeld 	ccid_worker_request(ccid);
1974a61ed2ceSHans Rosenfeld }
1975a61ed2ceSHans Rosenfeld 
1976a61ed2ceSHans Rosenfeld static void
ccid_intr_pipe_cb(usb_pipe_handle_t ph,usb_intr_req_t * uirp)1977a61ed2ceSHans Rosenfeld ccid_intr_pipe_cb(usb_pipe_handle_t ph, usb_intr_req_t *uirp)
1978a61ed2ceSHans Rosenfeld {
1979a61ed2ceSHans Rosenfeld 	mblk_t *mp;
1980a61ed2ceSHans Rosenfeld 	size_t msglen, explen;
1981a61ed2ceSHans Rosenfeld 	uint_t i;
1982a61ed2ceSHans Rosenfeld 	boolean_t change;
1983a61ed2ceSHans Rosenfeld 	ccid_intr_hwerr_t ccid_hwerr;
1984a61ed2ceSHans Rosenfeld 	ccid_t *ccid = (ccid_t *)uirp->intr_client_private;
1985a61ed2ceSHans Rosenfeld 
1986a61ed2ceSHans Rosenfeld 	mp = uirp->intr_data;
1987a61ed2ceSHans Rosenfeld 	if (mp == NULL)
1988a61ed2ceSHans Rosenfeld 		goto done;
1989a61ed2ceSHans Rosenfeld 
1990a61ed2ceSHans Rosenfeld 	msglen = msgsize(mp);
1991a61ed2ceSHans Rosenfeld 	if (msglen == 0)
1992a61ed2ceSHans Rosenfeld 		goto done;
1993a61ed2ceSHans Rosenfeld 
1994a61ed2ceSHans Rosenfeld 	switch (mp->b_rptr[0]) {
1995a61ed2ceSHans Rosenfeld 	case CCID_INTR_CODE_SLOT_CHANGE:
1996a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
1997a61ed2ceSHans Rosenfeld 		ccid->ccid_stats.cst_intr_slot_change++;
1998a61ed2ceSHans Rosenfeld 
1999a61ed2ceSHans Rosenfeld 		explen = 1 + ((2 * ccid->ccid_nslots + (NBBY-1)) / NBBY);
2000a61ed2ceSHans Rosenfeld 		if (msglen < explen) {
2001a61ed2ceSHans Rosenfeld 			ccid->ccid_stats.cst_intr_inval++;
2002a61ed2ceSHans Rosenfeld 			mutex_exit(&ccid->ccid_mutex);
2003a61ed2ceSHans Rosenfeld 			goto done;
2004a61ed2ceSHans Rosenfeld 		}
2005a61ed2ceSHans Rosenfeld 
2006a61ed2ceSHans Rosenfeld 		change = B_FALSE;
2007a61ed2ceSHans Rosenfeld 		for (i = 0; i < ccid->ccid_nslots; i++) {
2008a61ed2ceSHans Rosenfeld 			uint_t byte = (i * 2 / NBBY) + 1;
2009a61ed2ceSHans Rosenfeld 			uint_t shift = i * 2 % NBBY;
2010a61ed2ceSHans Rosenfeld 			uint_t present = 1 << shift;
2011a61ed2ceSHans Rosenfeld 			uint_t delta = 2 << shift;
2012a61ed2ceSHans Rosenfeld 
2013a61ed2ceSHans Rosenfeld 			if (mp->b_rptr[byte] & delta) {
2014a61ed2ceSHans Rosenfeld 				ccid_slot_t *slot = &ccid->ccid_slots[i];
2015a61ed2ceSHans Rosenfeld 
2016a61ed2ceSHans Rosenfeld 				slot->cs_flags &= ~CCID_SLOT_F_INTR_MASK;
2017a61ed2ceSHans Rosenfeld 				slot->cs_flags |= CCID_SLOT_F_CHANGED;
2018a61ed2ceSHans Rosenfeld 				if (mp->b_rptr[byte] & present) {
2019a61ed2ceSHans Rosenfeld 					slot->cs_flags |= CCID_SLOT_F_INTR_ADD;
2020a61ed2ceSHans Rosenfeld 				} else {
2021a61ed2ceSHans Rosenfeld 					slot->cs_flags |= CCID_SLOT_F_INTR_GONE;
2022a61ed2ceSHans Rosenfeld 				}
2023a61ed2ceSHans Rosenfeld 				change = B_TRUE;
2024a61ed2ceSHans Rosenfeld 			}
2025a61ed2ceSHans Rosenfeld 		}
2026a61ed2ceSHans Rosenfeld 
2027a61ed2ceSHans Rosenfeld 		if (change) {
2028a61ed2ceSHans Rosenfeld 			ccid_worker_request(ccid);
2029a61ed2ceSHans Rosenfeld 		}
2030a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2031a61ed2ceSHans Rosenfeld 		break;
2032a61ed2ceSHans Rosenfeld 	case CCID_INTR_CODE_HW_ERROR:
2033a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2034a61ed2ceSHans Rosenfeld 		ccid->ccid_stats.cst_intr_hwerr++;
2035a61ed2ceSHans Rosenfeld 
2036a61ed2ceSHans Rosenfeld 		if (msglen < sizeof (ccid_intr_hwerr_t)) {
2037a61ed2ceSHans Rosenfeld 			ccid->ccid_stats.cst_intr_inval++;
2038a61ed2ceSHans Rosenfeld 			mutex_exit(&ccid->ccid_mutex);
2039a61ed2ceSHans Rosenfeld 			goto done;
2040a61ed2ceSHans Rosenfeld 		}
2041a61ed2ceSHans Rosenfeld 
2042a61ed2ceSHans Rosenfeld 		bcopy(mp->b_rptr, &ccid_hwerr, sizeof (ccid_intr_hwerr_t));
2043a61ed2ceSHans Rosenfeld 		ccid_hw_error(ccid, &ccid_hwerr);
2044a61ed2ceSHans Rosenfeld 
2045a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2046a61ed2ceSHans Rosenfeld 		break;
2047a61ed2ceSHans Rosenfeld 	default:
2048a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2049a61ed2ceSHans Rosenfeld 		ccid->ccid_stats.cst_intr_unknown++;
2050a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2051a61ed2ceSHans Rosenfeld 		break;
2052a61ed2ceSHans Rosenfeld 	}
2053a61ed2ceSHans Rosenfeld 
2054a61ed2ceSHans Rosenfeld done:
2055a61ed2ceSHans Rosenfeld 	usb_free_intr_req(uirp);
2056a61ed2ceSHans Rosenfeld }
2057a61ed2ceSHans Rosenfeld 
2058a61ed2ceSHans Rosenfeld static void
ccid_intr_pipe_except_cb(usb_pipe_handle_t ph,usb_intr_req_t * uirp)2059a61ed2ceSHans Rosenfeld ccid_intr_pipe_except_cb(usb_pipe_handle_t ph, usb_intr_req_t *uirp)
2060a61ed2ceSHans Rosenfeld {
2061a61ed2ceSHans Rosenfeld 	ccid_t *ccid = (ccid_t *)uirp->intr_client_private;
2062a61ed2ceSHans Rosenfeld 
2063a61ed2ceSHans Rosenfeld 	ccid->ccid_stats.cst_intr_errs++;
2064a61ed2ceSHans Rosenfeld 	switch (uirp->intr_completion_reason) {
2065a61ed2ceSHans Rosenfeld 	case USB_CR_PIPE_RESET:
2066a61ed2ceSHans Rosenfeld 	case USB_CR_NO_RESOURCES:
2067a61ed2ceSHans Rosenfeld 		ccid->ccid_stats.cst_intr_restart++;
2068a61ed2ceSHans Rosenfeld 		ccid_intr_poll_init(ccid);
2069a61ed2ceSHans Rosenfeld 		break;
2070a61ed2ceSHans Rosenfeld 	default:
2071a61ed2ceSHans Rosenfeld 		break;
2072a61ed2ceSHans Rosenfeld 	}
2073a61ed2ceSHans Rosenfeld 	usb_free_intr_req(uirp);
2074a61ed2ceSHans Rosenfeld }
2075a61ed2ceSHans Rosenfeld 
2076a61ed2ceSHans Rosenfeld /*
2077a61ed2ceSHans Rosenfeld  * Clean up all the state associated with this slot and its ICC.
2078a61ed2ceSHans Rosenfeld  */
2079a61ed2ceSHans Rosenfeld static void
ccid_slot_teardown(ccid_t * ccid,ccid_slot_t * slot,boolean_t signal)2080a61ed2ceSHans Rosenfeld ccid_slot_teardown(ccid_t *ccid, ccid_slot_t *slot, boolean_t signal)
2081a61ed2ceSHans Rosenfeld {
2082a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2083a61ed2ceSHans Rosenfeld 
2084a61ed2ceSHans Rosenfeld 	if (slot->cs_icc.icc_fini != NULL) {
2085a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_fini(ccid, slot);
2086a61ed2ceSHans Rosenfeld 	}
2087a61ed2ceSHans Rosenfeld 
2088a61ed2ceSHans Rosenfeld 	atr_data_reset(slot->cs_icc.icc_atr_data);
2089a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_protocols = ATR_P_NONE;
2090a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_cur_protocol = ATR_P_NONE;
2091a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_init = NULL;
2092a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_tx = NULL;
2093a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_complete = NULL;
2094a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_teardown = NULL;
2095a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_fini = NULL;
2096a61ed2ceSHans Rosenfeld 
2097a61ed2ceSHans Rosenfeld 	slot->cs_voltage = 0;
2098a61ed2ceSHans Rosenfeld 	freemsgchain(slot->cs_atr);
2099a61ed2ceSHans Rosenfeld 	slot->cs_atr = NULL;
2100a61ed2ceSHans Rosenfeld 
2101a61ed2ceSHans Rosenfeld 	if (signal && slot->cs_excl_minor != NULL) {
2102a61ed2ceSHans Rosenfeld 		pollwakeup(&slot->cs_excl_minor->cm_pollhead, POLLHUP);
2103a61ed2ceSHans Rosenfeld 	}
2104a61ed2ceSHans Rosenfeld }
2105a61ed2ceSHans Rosenfeld 
2106a61ed2ceSHans Rosenfeld /*
2107a61ed2ceSHans Rosenfeld  * Wait for teardown of outstanding user I/O.
2108a61ed2ceSHans Rosenfeld  */
2109a61ed2ceSHans Rosenfeld static void
ccid_slot_io_teardown(ccid_t * ccid,ccid_slot_t * slot)2110a61ed2ceSHans Rosenfeld ccid_slot_io_teardown(ccid_t *ccid, ccid_slot_t *slot)
2111a61ed2ceSHans Rosenfeld {
2112a61ed2ceSHans Rosenfeld 	/*
2113a61ed2ceSHans Rosenfeld 	 * If there is outstanding user I/O, then we need to go ahead and take
2114a61ed2ceSHans Rosenfeld 	 * care of that. Once this function returns, the user I/O will have been
2115a61ed2ceSHans Rosenfeld 	 * dealt with; however, before we can tear down things, we need to make
2116a61ed2ceSHans Rosenfeld 	 * sure that the logical I/O has been completed.
2117a61ed2ceSHans Rosenfeld 	 */
2118a61ed2ceSHans Rosenfeld 	if (slot->cs_icc.icc_teardown != NULL) {
2119a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_teardown(ccid, slot, ENXIO);
2120a61ed2ceSHans Rosenfeld 	}
2121a61ed2ceSHans Rosenfeld 
2122a61ed2ceSHans Rosenfeld 	while ((slot->cs_flags & CCID_SLOT_F_NEED_IO_TEARDOWN) != 0) {
2123a61ed2ceSHans Rosenfeld 		cv_wait(&slot->cs_io.ci_cv, &ccid->ccid_mutex);
2124a61ed2ceSHans Rosenfeld 	}
2125a61ed2ceSHans Rosenfeld }
2126a61ed2ceSHans Rosenfeld 
2127a61ed2ceSHans Rosenfeld /*
2128a61ed2ceSHans Rosenfeld  * The given CCID slot has been inactivated. Clean up.
2129a61ed2ceSHans Rosenfeld  */
2130a61ed2ceSHans Rosenfeld static void
ccid_slot_inactive(ccid_t * ccid,ccid_slot_t * slot)2131a61ed2ceSHans Rosenfeld ccid_slot_inactive(ccid_t *ccid, ccid_slot_t *slot)
2132a61ed2ceSHans Rosenfeld {
2133a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2134a61ed2ceSHans Rosenfeld 
2135a61ed2ceSHans Rosenfeld 	slot->cs_flags &= ~CCID_SLOT_F_ACTIVE;
2136a61ed2ceSHans Rosenfeld 
2137a61ed2ceSHans Rosenfeld 	ccid_slot_io_teardown(ccid, slot);
2138a61ed2ceSHans Rosenfeld 
2139a61ed2ceSHans Rosenfeld 	/*
2140a61ed2ceSHans Rosenfeld 	 * Now that we've finished completely waiting for the logical I/O to be
2141a61ed2ceSHans Rosenfeld 	 * torn down, it's safe for us to proceed with the rest of the needed
2142a61ed2ceSHans Rosenfeld 	 * tear down.
2143a61ed2ceSHans Rosenfeld 	 */
2144a61ed2ceSHans Rosenfeld 	ccid_slot_teardown(ccid, slot, B_TRUE);
2145a61ed2ceSHans Rosenfeld }
2146a61ed2ceSHans Rosenfeld 
2147a61ed2ceSHans Rosenfeld /*
2148a61ed2ceSHans Rosenfeld  * The given CCID slot has been removed. Clean up.
2149a61ed2ceSHans Rosenfeld  */
2150a61ed2ceSHans Rosenfeld static void
ccid_slot_removed(ccid_t * ccid,ccid_slot_t * slot,boolean_t notify)2151a61ed2ceSHans Rosenfeld ccid_slot_removed(ccid_t *ccid, ccid_slot_t *slot, boolean_t notify)
2152a61ed2ceSHans Rosenfeld {
2153a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2154a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_PRESENT) == 0) {
2155a61ed2ceSHans Rosenfeld 		VERIFY0(slot->cs_flags & CCID_SLOT_F_ACTIVE);
2156a61ed2ceSHans Rosenfeld 		return;
2157a61ed2ceSHans Rosenfeld 	}
2158a61ed2ceSHans Rosenfeld 
2159a61ed2ceSHans Rosenfeld 	/*
2160a61ed2ceSHans Rosenfeld 	 * This slot is gone, mark the flags accordingly.
2161a61ed2ceSHans Rosenfeld 	 */
2162a61ed2ceSHans Rosenfeld 	slot->cs_flags &= ~CCID_SLOT_F_PRESENT;
2163a61ed2ceSHans Rosenfeld 
2164a61ed2ceSHans Rosenfeld 	ccid_slot_inactive(ccid, slot);
2165a61ed2ceSHans Rosenfeld }
2166a61ed2ceSHans Rosenfeld 
2167a61ed2ceSHans Rosenfeld static void
ccid_slot_setup_functions(ccid_t * ccid,ccid_slot_t * slot)2168a61ed2ceSHans Rosenfeld ccid_slot_setup_functions(ccid_t *ccid, ccid_slot_t *slot)
2169a61ed2ceSHans Rosenfeld {
2170a61ed2ceSHans Rosenfeld 	uint_t bits = CCID_CLASS_F_SHORT_APDU_XCHG | CCID_CLASS_F_EXT_APDU_XCHG;
2171a61ed2ceSHans Rosenfeld 
2172a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_init = NULL;
2173a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_tx = NULL;
2174a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_complete = NULL;
2175a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_teardown = NULL;
2176a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_fini = NULL;
2177a61ed2ceSHans Rosenfeld 
2178a61ed2ceSHans Rosenfeld 	switch (ccid->ccid_class.ccd_dwFeatures & bits) {
2179a61ed2ceSHans Rosenfeld 	case CCID_CLASS_F_SHORT_APDU_XCHG:
2180a61ed2ceSHans Rosenfeld 	case CCID_CLASS_F_EXT_APDU_XCHG:
2181a61ed2ceSHans Rosenfeld 		/*
2182a61ed2ceSHans Rosenfeld 		 * Readers with extended APDU support always also support
2183a61ed2ceSHans Rosenfeld 		 * short APDUs. We only ever use short APDUs.
2184a61ed2ceSHans Rosenfeld 		 */
2185a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_tx = ccid_write_apdu;
2186a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_complete = ccid_complete_apdu;
2187a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_teardown = ccid_teardown_apdu;
2188a61ed2ceSHans Rosenfeld 		break;
2189a61ed2ceSHans Rosenfeld 	default:
2190a61ed2ceSHans Rosenfeld 		break;
2191a61ed2ceSHans Rosenfeld 	}
2192a61ed2ceSHans Rosenfeld 
2193a61ed2ceSHans Rosenfeld 	/*
2194a61ed2ceSHans Rosenfeld 	 * When we don't have a supported tx function, we don't want to end
2195a61ed2ceSHans Rosenfeld 	 * up blocking attach. It's important we attach so that users can try
2196a61ed2ceSHans Rosenfeld 	 * and determine information about the ICC and reader.
2197a61ed2ceSHans Rosenfeld 	 */
2198a61ed2ceSHans Rosenfeld 	if (slot->cs_icc.icc_tx == NULL) {
2199a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!CCID does not support I/O transfers to ICC");
2200a61ed2ceSHans Rosenfeld 	}
2201a61ed2ceSHans Rosenfeld }
2202a61ed2ceSHans Rosenfeld 
2203a61ed2ceSHans Rosenfeld /*
2204a61ed2ceSHans Rosenfeld  * We have an ICC present in a slot. We require that the reader does all
2205a61ed2ceSHans Rosenfeld  * protocol and parameter related initializations for us. Just parse the ATR
2206a61ed2ceSHans Rosenfeld  * for our own use and use GET_PARAMS to query the parameters the reader set
2207a61ed2ceSHans Rosenfeld  * up for us.
2208a61ed2ceSHans Rosenfeld  */
2209a61ed2ceSHans Rosenfeld static boolean_t
ccid_slot_params_init(ccid_t * ccid,ccid_slot_t * slot,mblk_t * atr)2210a61ed2ceSHans Rosenfeld ccid_slot_params_init(ccid_t *ccid, ccid_slot_t *slot, mblk_t *atr)
2211a61ed2ceSHans Rosenfeld {
2212a61ed2ceSHans Rosenfeld 	int ret;
2213a61ed2ceSHans Rosenfeld 	atr_parsecode_t p;
2214a61ed2ceSHans Rosenfeld 	atr_protocol_t prot;
2215a61ed2ceSHans Rosenfeld 	atr_data_t *data;
2216a61ed2ceSHans Rosenfeld 
2217a61ed2ceSHans Rosenfeld 	/*
2218a61ed2ceSHans Rosenfeld 	 * Use the slot's atr data structure. This is only used when we're in
2219a61ed2ceSHans Rosenfeld 	 * the worker context, so it should be safe to access in a lockless
2220a61ed2ceSHans Rosenfeld 	 * fashion.
2221a61ed2ceSHans Rosenfeld 	 */
2222a61ed2ceSHans Rosenfeld 	data = slot->cs_icc.icc_atr_data;
2223a61ed2ceSHans Rosenfeld 	atr_data_reset(data);
2224a61ed2ceSHans Rosenfeld 	if ((p = atr_parse(atr->b_rptr, msgsize(atr), data)) != ATR_CODE_OK) {
2225a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to parse ATR data from slot %d: %s",
2226a61ed2ceSHans Rosenfeld 		    slot->cs_slotno, atr_strerror(p));
2227a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2228a61ed2ceSHans Rosenfeld 	}
2229a61ed2ceSHans Rosenfeld 
2230a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_get_parameters(ccid, slot, &prot,
2231a61ed2ceSHans Rosenfeld 	    &slot->cs_icc.icc_params)) != 0) {
2232a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to get parameters for slot %u: %d",
2233a61ed2ceSHans Rosenfeld 		    slot->cs_slotno, ret);
2234a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2235a61ed2ceSHans Rosenfeld 	}
2236a61ed2ceSHans Rosenfeld 
2237a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_protocols = atr_supported_protocols(data);
2238a61ed2ceSHans Rosenfeld 	slot->cs_icc.icc_cur_protocol = prot;
2239a61ed2ceSHans Rosenfeld 
2240a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & (CCID_F_NEEDS_PPS | CCID_F_NEEDS_PARAMS |
2241a61ed2ceSHans Rosenfeld 	    CCID_F_NEEDS_DATAFREQ)) != 0) {
2242a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!CCID reader does not support required "
2243a61ed2ceSHans Rosenfeld 		    "protocol/parameter setup automation");
2244a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2245a61ed2ceSHans Rosenfeld 	}
2246a61ed2ceSHans Rosenfeld 
2247a61ed2ceSHans Rosenfeld 	return (B_TRUE);
2248a61ed2ceSHans Rosenfeld }
2249a61ed2ceSHans Rosenfeld 
2250a61ed2ceSHans Rosenfeld /*
2251a61ed2ceSHans Rosenfeld  * Set up the ICC function parameters and initialize the ICC engine.
2252a61ed2ceSHans Rosenfeld  */
2253a61ed2ceSHans Rosenfeld static boolean_t
ccid_slot_prot_init(ccid_t * ccid,ccid_slot_t * slot)2254a61ed2ceSHans Rosenfeld ccid_slot_prot_init(ccid_t *ccid, ccid_slot_t *slot)
2255a61ed2ceSHans Rosenfeld {
2256a61ed2ceSHans Rosenfeld 	ccid_slot_setup_functions(ccid, slot);
2257a61ed2ceSHans Rosenfeld 
2258a61ed2ceSHans Rosenfeld 	if (slot->cs_icc.icc_init != NULL) {
2259a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_init(ccid, slot);
2260a61ed2ceSHans Rosenfeld 	}
2261a61ed2ceSHans Rosenfeld 
2262a61ed2ceSHans Rosenfeld 	return (B_TRUE);
2263a61ed2ceSHans Rosenfeld }
2264a61ed2ceSHans Rosenfeld 
2265a61ed2ceSHans Rosenfeld static int
ccid_slot_power_on(ccid_t * ccid,ccid_slot_t * slot,ccid_class_voltage_t volts,mblk_t ** atr)2266a61ed2ceSHans Rosenfeld ccid_slot_power_on(ccid_t *ccid, ccid_slot_t *slot, ccid_class_voltage_t volts,
2267a61ed2ceSHans Rosenfeld     mblk_t **atr)
2268a61ed2ceSHans Rosenfeld {
2269a61ed2ceSHans Rosenfeld 	int ret;
2270a61ed2ceSHans Rosenfeld 
2271a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2272a61ed2ceSHans Rosenfeld 
2273a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
2274a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_power_on(ccid, slot, volts, atr)) != 0) {
2275a61ed2ceSHans Rosenfeld 		/*
2276a61ed2ceSHans Rosenfeld 		 * If we got ENXIO, then we know that there is no ICC
2277a61ed2ceSHans Rosenfeld 		 * present. This could happen for a number of reasons.
2278a61ed2ceSHans Rosenfeld 		 * For example, we could have just started up and no
2279a61ed2ceSHans Rosenfeld 		 * card was plugged in (we default to assuming that one
2280a61ed2ceSHans Rosenfeld 		 * is). Also, some readers won't really tell us that
2281a61ed2ceSHans Rosenfeld 		 * nothing is there until after the power on fails,
2282a61ed2ceSHans Rosenfeld 		 * hence why we don't bother with doing a status check
2283a61ed2ceSHans Rosenfeld 		 * and just try to power on.
2284a61ed2ceSHans Rosenfeld 		 */
2285a61ed2ceSHans Rosenfeld 		if (ret == ENXIO) {
2286a61ed2ceSHans Rosenfeld 			mutex_enter(&ccid->ccid_mutex);
2287a61ed2ceSHans Rosenfeld 			slot->cs_flags &= ~CCID_SLOT_F_PRESENT;
2288a61ed2ceSHans Rosenfeld 			return (ret);
2289a61ed2ceSHans Rosenfeld 		}
2290a61ed2ceSHans Rosenfeld 
2291a61ed2ceSHans Rosenfeld 		/*
2292a61ed2ceSHans Rosenfeld 		 * If we fail to power off the card, check to make sure
2293a61ed2ceSHans Rosenfeld 		 * it hasn't been removed.
2294a61ed2ceSHans Rosenfeld 		 */
2295a61ed2ceSHans Rosenfeld 		if (ccid_command_power_off(ccid, slot) == ENXIO) {
2296a61ed2ceSHans Rosenfeld 			mutex_enter(&ccid->ccid_mutex);
2297a61ed2ceSHans Rosenfeld 			slot->cs_flags &= ~CCID_SLOT_F_PRESENT;
2298a61ed2ceSHans Rosenfeld 			return (ENXIO);
2299a61ed2ceSHans Rosenfeld 		}
2300a61ed2ceSHans Rosenfeld 
2301a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2302a61ed2ceSHans Rosenfeld 		return (ret);
2303a61ed2ceSHans Rosenfeld 	}
2304a61ed2ceSHans Rosenfeld 
2305a61ed2ceSHans Rosenfeld 	if (!ccid_slot_params_init(ccid, slot, *atr)) {
2306a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to set slot paramters for ICC");
2307a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2308a61ed2ceSHans Rosenfeld 		return (ENOTSUP);
2309a61ed2ceSHans Rosenfeld 	}
2310a61ed2ceSHans Rosenfeld 
2311a61ed2ceSHans Rosenfeld 	if (!ccid_slot_prot_init(ccid, slot)) {
2312a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to setup protocol for ICC");
2313a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2314a61ed2ceSHans Rosenfeld 		return (ENOTSUP);
2315a61ed2ceSHans Rosenfeld 	}
2316a61ed2ceSHans Rosenfeld 
2317a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
2318a61ed2ceSHans Rosenfeld 	return (0);
2319a61ed2ceSHans Rosenfeld }
2320a61ed2ceSHans Rosenfeld 
2321a61ed2ceSHans Rosenfeld static int
ccid_slot_power_off(ccid_t * ccid,ccid_slot_t * slot)2322a61ed2ceSHans Rosenfeld ccid_slot_power_off(ccid_t *ccid, ccid_slot_t *slot)
2323a61ed2ceSHans Rosenfeld {
2324a61ed2ceSHans Rosenfeld 	int ret;
2325a61ed2ceSHans Rosenfeld 
2326a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2327a61ed2ceSHans Rosenfeld 
2328a61ed2ceSHans Rosenfeld 	ccid_slot_io_teardown(ccid, slot);
2329a61ed2ceSHans Rosenfeld 
2330a61ed2ceSHans Rosenfeld 	/*
2331a61ed2ceSHans Rosenfeld 	 * Now that we've finished completely waiting for the logical I/O to be
2332a61ed2ceSHans Rosenfeld 	 * torn down, try and power off the ICC.
2333a61ed2ceSHans Rosenfeld 	 */
2334a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
2335a61ed2ceSHans Rosenfeld 	ret = ccid_command_power_off(ccid, slot);
2336a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
2337a61ed2ceSHans Rosenfeld 
2338a61ed2ceSHans Rosenfeld 	if (ret != 0)
2339a61ed2ceSHans Rosenfeld 		return (ret);
2340a61ed2ceSHans Rosenfeld 
2341a61ed2ceSHans Rosenfeld 	ccid_slot_inactive(ccid, slot);
2342a61ed2ceSHans Rosenfeld 
2343a61ed2ceSHans Rosenfeld 	return (ret);
2344a61ed2ceSHans Rosenfeld }
2345a61ed2ceSHans Rosenfeld 
2346a61ed2ceSHans Rosenfeld static int
ccid_slot_inserted(ccid_t * ccid,ccid_slot_t * slot)2347a61ed2ceSHans Rosenfeld ccid_slot_inserted(ccid_t *ccid, ccid_slot_t *slot)
2348a61ed2ceSHans Rosenfeld {
2349a61ed2ceSHans Rosenfeld 	uint_t nvolts = 4;
2350a61ed2ceSHans Rosenfeld 	uint_t cvolt = 0;
2351a61ed2ceSHans Rosenfeld 	mblk_t *atr = NULL;
2352a61ed2ceSHans Rosenfeld 	ccid_class_voltage_t volts[4] = { CCID_CLASS_VOLT_AUTO,
2353a61ed2ceSHans Rosenfeld 	    CCID_CLASS_VOLT_5_0, CCID_CLASS_VOLT_3_0, CCID_CLASS_VOLT_1_8 };
2354a61ed2ceSHans Rosenfeld 
2355a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2356a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
2357a61ed2ceSHans Rosenfeld 		return (0);
2358a61ed2ceSHans Rosenfeld 	}
2359a61ed2ceSHans Rosenfeld 
2360a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0) {
2361a61ed2ceSHans Rosenfeld 		return (0);
2362a61ed2ceSHans Rosenfeld 	}
2363a61ed2ceSHans Rosenfeld 
2364a61ed2ceSHans Rosenfeld 	slot->cs_flags |= CCID_SLOT_F_PRESENT;
2365a61ed2ceSHans Rosenfeld 
2366a61ed2ceSHans Rosenfeld 	/*
2367a61ed2ceSHans Rosenfeld 	 * Now, we need to activate this ccid device before we can do anything
2368a61ed2ceSHans Rosenfeld 	 * with it. First, power on the device. There are two hardware features
2369a61ed2ceSHans Rosenfeld 	 * which may be at play. There may be automatic voltage detection and
2370a61ed2ceSHans Rosenfeld 	 * automatic activation on insertion. In theory, when either of those
2371a61ed2ceSHans Rosenfeld 	 * are present, we should always try to use the auto voltage.
2372a61ed2ceSHans Rosenfeld 	 *
2373a61ed2ceSHans Rosenfeld 	 * What's less clear in the specification is if the Auto-Voltage
2374a61ed2ceSHans Rosenfeld 	 * property is present is if we should try manual voltages or not. For
2375a61ed2ceSHans Rosenfeld 	 * the moment we do.
2376a61ed2ceSHans Rosenfeld 	 */
2377a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_class.ccd_dwFeatures &
2378a61ed2ceSHans Rosenfeld 	    (CCID_CLASS_F_AUTO_ICC_ACTIVATE | CCID_CLASS_F_AUTO_ICC_VOLTAGE)) ==
2379a61ed2ceSHans Rosenfeld 	    0) {
2380a61ed2ceSHans Rosenfeld 		/* Skip auto-voltage */
2381a61ed2ceSHans Rosenfeld 		cvolt++;
2382a61ed2ceSHans Rosenfeld 	}
2383a61ed2ceSHans Rosenfeld 
2384a61ed2ceSHans Rosenfeld 	for (; cvolt < nvolts; cvolt++) {
2385a61ed2ceSHans Rosenfeld 		int ret;
2386a61ed2ceSHans Rosenfeld 
2387a61ed2ceSHans Rosenfeld 		if (volts[cvolt] != CCID_CLASS_VOLT_AUTO &&
2388a61ed2ceSHans Rosenfeld 		    (ccid->ccid_class.ccd_bVoltageSupport & volts[cvolt]) ==
2389a61ed2ceSHans Rosenfeld 		    0) {
2390a61ed2ceSHans Rosenfeld 			continue;
2391a61ed2ceSHans Rosenfeld 		}
2392a61ed2ceSHans Rosenfeld 
2393a61ed2ceSHans Rosenfeld 		ret = ccid_slot_power_on(ccid, slot, volts[cvolt], &atr);
2394a61ed2ceSHans Rosenfeld 		if (ret != 0) {
2395a61ed2ceSHans Rosenfeld 			freemsg(atr);
2396a61ed2ceSHans Rosenfeld 			atr = NULL;
2397a61ed2ceSHans Rosenfeld 			continue;
2398a61ed2ceSHans Rosenfeld 		}
2399a61ed2ceSHans Rosenfeld 
2400a61ed2ceSHans Rosenfeld 		break;
2401a61ed2ceSHans Rosenfeld 	}
2402a61ed2ceSHans Rosenfeld 
2403a61ed2ceSHans Rosenfeld 	if (cvolt >= nvolts) {
2404a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to activate and power on ICC, no "
2405a61ed2ceSHans Rosenfeld 		    "supported voltages found");
2406a61ed2ceSHans Rosenfeld 		goto notsup;
2407a61ed2ceSHans Rosenfeld 	}
2408a61ed2ceSHans Rosenfeld 
2409a61ed2ceSHans Rosenfeld 	slot->cs_voltage = volts[cvolt];
2410a61ed2ceSHans Rosenfeld 	slot->cs_atr = atr;
2411a61ed2ceSHans Rosenfeld 	slot->cs_flags |= CCID_SLOT_F_ACTIVE;
2412a61ed2ceSHans Rosenfeld 
2413a61ed2ceSHans Rosenfeld 	ccid_slot_pollout_signal(slot);
2414a61ed2ceSHans Rosenfeld 
2415a61ed2ceSHans Rosenfeld 	return (0);
2416a61ed2ceSHans Rosenfeld 
2417a61ed2ceSHans Rosenfeld notsup:
2418a61ed2ceSHans Rosenfeld 	freemsg(atr);
2419a61ed2ceSHans Rosenfeld 	ccid_slot_teardown(ccid, slot, B_FALSE);
2420a61ed2ceSHans Rosenfeld 	return (ENOTSUP);
2421a61ed2ceSHans Rosenfeld }
2422a61ed2ceSHans Rosenfeld 
2423a61ed2ceSHans Rosenfeld static int
ccid_slot_warm_reset(ccid_t * ccid,ccid_slot_t * slot)2424a61ed2ceSHans Rosenfeld ccid_slot_warm_reset(ccid_t *ccid, ccid_slot_t *slot)
2425a61ed2ceSHans Rosenfeld {
2426a61ed2ceSHans Rosenfeld 	int ret;
2427a61ed2ceSHans Rosenfeld 	mblk_t *atr;
2428a61ed2ceSHans Rosenfeld 	ccid_class_voltage_t voltage;
2429a61ed2ceSHans Rosenfeld 
2430a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2431a61ed2ceSHans Rosenfeld 
2432a61ed2ceSHans Rosenfeld 	ccid_slot_io_teardown(ccid, slot);
2433a61ed2ceSHans Rosenfeld 
2434a61ed2ceSHans Rosenfeld 	voltage = slot->cs_voltage;
2435a61ed2ceSHans Rosenfeld 
2436a61ed2ceSHans Rosenfeld 	ccid_slot_teardown(ccid, slot, B_FALSE);
2437a61ed2ceSHans Rosenfeld 
2438a61ed2ceSHans Rosenfeld 	ret = ccid_slot_power_on(ccid, slot, voltage, &atr);
2439a61ed2ceSHans Rosenfeld 	if (ret != 0) {
2440a61ed2ceSHans Rosenfeld 		freemsg(atr);
2441a61ed2ceSHans Rosenfeld 		return (ret);
2442a61ed2ceSHans Rosenfeld 	}
2443a61ed2ceSHans Rosenfeld 
2444a61ed2ceSHans Rosenfeld 	slot->cs_voltage = voltage;
2445a61ed2ceSHans Rosenfeld 	slot->cs_atr = atr;
2446a61ed2ceSHans Rosenfeld 
2447a61ed2ceSHans Rosenfeld 	return (ret);
2448a61ed2ceSHans Rosenfeld }
2449a61ed2ceSHans Rosenfeld 
2450a61ed2ceSHans Rosenfeld static boolean_t
ccid_slot_reset(ccid_t * ccid,ccid_slot_t * slot)2451a61ed2ceSHans Rosenfeld ccid_slot_reset(ccid_t *ccid, ccid_slot_t *slot)
2452a61ed2ceSHans Rosenfeld {
2453a61ed2ceSHans Rosenfeld 	int ret;
2454a61ed2ceSHans Rosenfeld 
2455a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2456a61ed2ceSHans Rosenfeld 	VERIFY(slot->cs_flags & CCID_SLOT_F_NEED_TXN_RESET);
2457a61ed2ceSHans Rosenfeld 	VERIFY(ccid->ccid_flags & CCID_F_WORKER_RUNNING);
2458a61ed2ceSHans Rosenfeld 
2459a61ed2ceSHans Rosenfeld 	if (ccid->ccid_flags & CCID_F_DEV_GONE_MASK)
2460a61ed2ceSHans Rosenfeld 		return (B_TRUE);
2461a61ed2ceSHans Rosenfeld 
2462a61ed2ceSHans Rosenfeld 	/*
2463a61ed2ceSHans Rosenfeld 	 * Power off the ICC. This will wait for logical I/O if needed.
2464a61ed2ceSHans Rosenfeld 	 */
2465a61ed2ceSHans Rosenfeld 	ret = ccid_slot_power_off(ccid, slot);
2466a61ed2ceSHans Rosenfeld 
2467a61ed2ceSHans Rosenfeld 	/*
2468a61ed2ceSHans Rosenfeld 	 * If we failed to power off the ICC because the ICC is removed, then
2469a61ed2ceSHans Rosenfeld 	 * just return that we failed, so that we can let the next lap clean
2470a61ed2ceSHans Rosenfeld 	 * things up by noting that the ICC has been removed.
2471a61ed2ceSHans Rosenfeld 	 */
2472a61ed2ceSHans Rosenfeld 	if (ret != 0 && ret == ENXIO) {
2473a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2474a61ed2ceSHans Rosenfeld 	}
2475a61ed2ceSHans Rosenfeld 
2476a61ed2ceSHans Rosenfeld 	if (ret != 0) {
2477a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to reset slot %d for next txn: %d; "
2478a61ed2ceSHans Rosenfeld 		    "taking another lap", slot->cs_slotno, ret);
2479a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2480a61ed2ceSHans Rosenfeld 	}
2481a61ed2ceSHans Rosenfeld 
2482a61ed2ceSHans Rosenfeld 	/*
2483a61ed2ceSHans Rosenfeld 	 * Mimic a slot insertion to power this back on. Don't worry about
2484a61ed2ceSHans Rosenfeld 	 * success or failure, because as far as we care for resetting it, we've
2485a61ed2ceSHans Rosenfeld 	 * done our duty once we've powered it off successfully.
2486a61ed2ceSHans Rosenfeld 	 */
2487a61ed2ceSHans Rosenfeld 	(void) ccid_slot_inserted(ccid, slot);
2488a61ed2ceSHans Rosenfeld 
2489a61ed2ceSHans Rosenfeld 	return (B_TRUE);
2490a61ed2ceSHans Rosenfeld }
2491a61ed2ceSHans Rosenfeld 
2492a61ed2ceSHans Rosenfeld /*
2493a61ed2ceSHans Rosenfeld  * We've been asked to perform some amount of work on the various slots that we
2494a61ed2ceSHans Rosenfeld  * have. This may be because the slot needs to be reset due to the completion of
2495a61ed2ceSHans Rosenfeld  * a transaction or it may be because an ICC inside of the slot has been
2496a61ed2ceSHans Rosenfeld  * removed.
2497a61ed2ceSHans Rosenfeld  */
2498a61ed2ceSHans Rosenfeld static void
ccid_worker(void * arg)2499a61ed2ceSHans Rosenfeld ccid_worker(void *arg)
2500a61ed2ceSHans Rosenfeld {
2501a61ed2ceSHans Rosenfeld 	uint_t i;
2502a61ed2ceSHans Rosenfeld 	ccid_t *ccid = arg;
2503a61ed2ceSHans Rosenfeld 
2504a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
2505a61ed2ceSHans Rosenfeld 	ccid->ccid_stats.cst_ndiscover++;
2506a61ed2ceSHans Rosenfeld 	ccid->ccid_stats.cst_lastdiscover = gethrtime();
2507a61ed2ceSHans Rosenfeld 	ccid->ccid_flags |= CCID_F_WORKER_RUNNING;
2508a61ed2ceSHans Rosenfeld 	ccid->ccid_flags &= ~CCID_F_WORKER_REQUESTED;
2509a61ed2ceSHans Rosenfeld 
2510a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_nslots; i++) {
2511a61ed2ceSHans Rosenfeld 		ccid_slot_t *slot = &ccid->ccid_slots[i];
2512a61ed2ceSHans Rosenfeld 		uint_t flags;
2513a61ed2ceSHans Rosenfeld 
2514a61ed2ceSHans Rosenfeld 		VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2515a61ed2ceSHans Rosenfeld 
2516a61ed2ceSHans Rosenfeld 		if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
2517a61ed2ceSHans Rosenfeld 			ccid->ccid_flags &= ~CCID_F_WORKER_MASK;
2518a61ed2ceSHans Rosenfeld 			mutex_exit(&ccid->ccid_mutex);
2519a61ed2ceSHans Rosenfeld 			return;
2520a61ed2ceSHans Rosenfeld 		}
2521a61ed2ceSHans Rosenfeld 
2522a61ed2ceSHans Rosenfeld 		/*
2523a61ed2ceSHans Rosenfeld 		 * Snapshot the flags before we start processing the worker. At
2524a61ed2ceSHans Rosenfeld 		 * this time we clear out all of the change flags as we'll be
2525a61ed2ceSHans Rosenfeld 		 * operating on the device. We do not clear the
2526a61ed2ceSHans Rosenfeld 		 * CCID_SLOT_F_NEED_TXN_RESET flag, as we want to make sure that
2527a61ed2ceSHans Rosenfeld 		 * this is maintained until we're done here.
2528a61ed2ceSHans Rosenfeld 		 */
2529a61ed2ceSHans Rosenfeld 		flags = slot->cs_flags & CCID_SLOT_F_WORK_MASK;
2530a61ed2ceSHans Rosenfeld 		slot->cs_flags &= ~CCID_SLOT_F_INTR_MASK;
2531a61ed2ceSHans Rosenfeld 
2532a61ed2ceSHans Rosenfeld 		if ((flags & CCID_SLOT_F_INTR_OVERCURRENT) != 0) {
2533a61ed2ceSHans Rosenfeld 			ccid_slot_inactive(ccid, slot);
2534a61ed2ceSHans Rosenfeld 		}
2535a61ed2ceSHans Rosenfeld 
2536a61ed2ceSHans Rosenfeld 		if ((flags & CCID_SLOT_F_CHANGED) != 0) {
2537a61ed2ceSHans Rosenfeld 			if (flags & CCID_SLOT_F_INTR_GONE) {
2538a61ed2ceSHans Rosenfeld 				ccid_slot_removed(ccid, slot, B_TRUE);
2539a61ed2ceSHans Rosenfeld 			} else {
2540a61ed2ceSHans Rosenfeld 				(void) ccid_slot_inserted(ccid, slot);
2541a61ed2ceSHans Rosenfeld 				if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) !=
2542a61ed2ceSHans Rosenfeld 				    0) {
2543a61ed2ceSHans Rosenfeld 					ccid_slot_excl_maybe_signal(slot);
2544a61ed2ceSHans Rosenfeld 				}
2545a61ed2ceSHans Rosenfeld 			}
2546a61ed2ceSHans Rosenfeld 			VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2547a61ed2ceSHans Rosenfeld 		}
2548a61ed2ceSHans Rosenfeld 
2549a61ed2ceSHans Rosenfeld 		if ((flags & CCID_SLOT_F_NEED_TXN_RESET) != 0) {
2550a61ed2ceSHans Rosenfeld 			/*
2551a61ed2ceSHans Rosenfeld 			 * If the CCID_SLOT_F_PRESENT flag is set, then we
2552a61ed2ceSHans Rosenfeld 			 * should attempt to power off and power on the ICC in
2553a61ed2ceSHans Rosenfeld 			 * an attempt to reset it. If this fails, trigger
2554a61ed2ceSHans Rosenfeld 			 * another worker that needs to operate.
2555a61ed2ceSHans Rosenfeld 			 */
2556a61ed2ceSHans Rosenfeld 			if ((slot->cs_flags & CCID_SLOT_F_PRESENT) != 0) {
2557a61ed2ceSHans Rosenfeld 				if (!ccid_slot_reset(ccid, slot)) {
2558a61ed2ceSHans Rosenfeld 					ccid_worker_request(ccid);
2559a61ed2ceSHans Rosenfeld 					continue;
2560a61ed2ceSHans Rosenfeld 				}
2561a61ed2ceSHans Rosenfeld 			}
2562a61ed2ceSHans Rosenfeld 
2563a61ed2ceSHans Rosenfeld 			VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2564a61ed2ceSHans Rosenfeld 			slot->cs_flags &= ~CCID_SLOT_F_NEED_TXN_RESET;
2565a61ed2ceSHans Rosenfeld 			/*
2566a61ed2ceSHans Rosenfeld 			 * Try to signal the next thread waiting for exclusive
2567a61ed2ceSHans Rosenfeld 			 * access.
2568a61ed2ceSHans Rosenfeld 			 */
2569a61ed2ceSHans Rosenfeld 			ccid_slot_excl_maybe_signal(slot);
2570a61ed2ceSHans Rosenfeld 		}
2571a61ed2ceSHans Rosenfeld 	}
2572a61ed2ceSHans Rosenfeld 
2573a61ed2ceSHans Rosenfeld 	/*
2574a61ed2ceSHans Rosenfeld 	 * If we have a request to operate again, delay before we consider this,
2575a61ed2ceSHans Rosenfeld 	 * to make sure we don't do too much work ourselves.
2576a61ed2ceSHans Rosenfeld 	 */
2577a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_WORKER_REQUESTED) != 0) {
2578a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2579a61ed2ceSHans Rosenfeld 		delay(drv_usectohz(1000) * 10);
2580a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2581a61ed2ceSHans Rosenfeld 	}
2582a61ed2ceSHans Rosenfeld 
2583a61ed2ceSHans Rosenfeld 	ccid->ccid_flags &= ~CCID_F_WORKER_RUNNING;
2584a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
2585a61ed2ceSHans Rosenfeld 		ccid->ccid_flags &= ~CCID_F_WORKER_REQUESTED;
2586a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2587a61ed2ceSHans Rosenfeld 		return;
2588a61ed2ceSHans Rosenfeld 	}
2589a61ed2ceSHans Rosenfeld 
2590a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_WORKER_REQUESTED) != 0) {
2591a61ed2ceSHans Rosenfeld 		(void) ddi_taskq_dispatch(ccid->ccid_taskq, ccid_worker, ccid,
2592a61ed2ceSHans Rosenfeld 		    DDI_SLEEP);
2593a61ed2ceSHans Rosenfeld 	}
2594a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
2595a61ed2ceSHans Rosenfeld }
2596a61ed2ceSHans Rosenfeld 
2597a61ed2ceSHans Rosenfeld static void
ccid_worker_request(ccid_t * ccid)2598a61ed2ceSHans Rosenfeld ccid_worker_request(ccid_t *ccid)
2599a61ed2ceSHans Rosenfeld {
2600a61ed2ceSHans Rosenfeld 	boolean_t run;
2601a61ed2ceSHans Rosenfeld 
2602a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
2603a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
2604a61ed2ceSHans Rosenfeld 		return;
2605a61ed2ceSHans Rosenfeld 	}
2606a61ed2ceSHans Rosenfeld 
2607a61ed2ceSHans Rosenfeld 	run = (ccid->ccid_flags & CCID_F_WORKER_MASK) == 0;
2608a61ed2ceSHans Rosenfeld 	ccid->ccid_flags |= CCID_F_WORKER_REQUESTED;
2609a61ed2ceSHans Rosenfeld 	if (run) {
2610a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2611a61ed2ceSHans Rosenfeld 		(void) ddi_taskq_dispatch(ccid->ccid_taskq, ccid_worker, ccid,
2612a61ed2ceSHans Rosenfeld 		    DDI_SLEEP);
2613a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2614a61ed2ceSHans Rosenfeld 	}
2615a61ed2ceSHans Rosenfeld }
2616a61ed2ceSHans Rosenfeld 
2617a61ed2ceSHans Rosenfeld static void
ccid_intr_restart_timeout(void * arg)2618a61ed2ceSHans Rosenfeld ccid_intr_restart_timeout(void *arg)
2619a61ed2ceSHans Rosenfeld {
2620a61ed2ceSHans Rosenfeld 	ccid_t *ccid = arg;
2621a61ed2ceSHans Rosenfeld 
2622a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
2623a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DEV_GONE_MASK) != 0) {
2624a61ed2ceSHans Rosenfeld 		ccid->ccid_poll_timeout = NULL;
2625a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2626a61ed2ceSHans Rosenfeld 	}
2627a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
2628a61ed2ceSHans Rosenfeld 
2629a61ed2ceSHans Rosenfeld 	ccid_intr_poll_init(ccid);
2630a61ed2ceSHans Rosenfeld }
2631a61ed2ceSHans Rosenfeld 
2632a61ed2ceSHans Rosenfeld /*
2633a61ed2ceSHans Rosenfeld  * Search for the current class descriptor from the configuration cloud and
2634a61ed2ceSHans Rosenfeld  * parse it for our use. We do this by first finding the current interface
2635a61ed2ceSHans Rosenfeld  * descriptor and expecting it to be one of the next descriptors.
2636a61ed2ceSHans Rosenfeld  */
2637a61ed2ceSHans Rosenfeld static boolean_t
ccid_parse_class_desc(ccid_t * ccid)2638a61ed2ceSHans Rosenfeld ccid_parse_class_desc(ccid_t *ccid)
2639a61ed2ceSHans Rosenfeld {
2640a61ed2ceSHans Rosenfeld 	uint_t i;
2641a61ed2ceSHans Rosenfeld 	size_t len, tlen;
2642a61ed2ceSHans Rosenfeld 	usb_client_dev_data_t *dp;
2643a61ed2ceSHans Rosenfeld 	usb_alt_if_data_t *alt;
2644a61ed2ceSHans Rosenfeld 
2645a61ed2ceSHans Rosenfeld 	/*
2646a61ed2ceSHans Rosenfeld 	 * Establish the target length we're looking for from usb_parse_data().
2647a61ed2ceSHans Rosenfeld 	 * Note that we cannot use the sizeof (ccid_class_descr_t) for this
2648a61ed2ceSHans Rosenfeld 	 * because that function does not know how to account for the padding at
2649a61ed2ceSHans Rosenfeld 	 * the end of the target structure (which is reasonble). So we manually
2650a61ed2ceSHans Rosenfeld 	 * figure out the number of bytes it should in theory write.
2651a61ed2ceSHans Rosenfeld 	 */
2652a61ed2ceSHans Rosenfeld 	tlen = offsetof(ccid_class_descr_t, ccd_bMaxCCIDBusySlots) +
2653a61ed2ceSHans Rosenfeld 	    sizeof (ccid->ccid_class.ccd_bMaxCCIDBusySlots);
2654a61ed2ceSHans Rosenfeld 	dp = ccid->ccid_dev_data;
2655a61ed2ceSHans Rosenfeld 	alt = &dp->dev_curr_cfg->cfg_if[dp->dev_curr_if].if_alt[0];
2656a61ed2ceSHans Rosenfeld 	for (i = 0; i < alt->altif_n_cvs; i++) {
2657a61ed2ceSHans Rosenfeld 		usb_cvs_data_t *cvs = &alt->altif_cvs[i];
2658a61ed2ceSHans Rosenfeld 		if (cvs->cvs_buf == NULL)
2659a61ed2ceSHans Rosenfeld 			continue;
2660a61ed2ceSHans Rosenfeld 		if (cvs->cvs_buf_len != CCID_DESCR_LENGTH)
2661a61ed2ceSHans Rosenfeld 			continue;
2662a61ed2ceSHans Rosenfeld 		if (cvs->cvs_buf[1] != CCID_DESCR_TYPE)
2663a61ed2ceSHans Rosenfeld 			continue;
2664a61ed2ceSHans Rosenfeld 		if ((len = usb_parse_data("ccscc3lcllc5lccscc", cvs->cvs_buf,
2665a61ed2ceSHans Rosenfeld 		    cvs->cvs_buf_len, &ccid->ccid_class,
2666a61ed2ceSHans Rosenfeld 		    sizeof (ccid->ccid_class))) >= tlen) {
2667a61ed2ceSHans Rosenfeld 			return (B_TRUE);
2668a61ed2ceSHans Rosenfeld 		}
2669a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to parse CCID class descriptor from "
2670a61ed2ceSHans Rosenfeld 		    "cvs %u, expected %lu bytes, received %lu", i, tlen, len);
2671a61ed2ceSHans Rosenfeld 	}
2672a61ed2ceSHans Rosenfeld 
2673a61ed2ceSHans Rosenfeld 	ccid_error(ccid, "!failed to find matching CCID class descriptor");
2674a61ed2ceSHans Rosenfeld 	return (B_FALSE);
2675a61ed2ceSHans Rosenfeld }
2676a61ed2ceSHans Rosenfeld 
2677a61ed2ceSHans Rosenfeld /*
2678a61ed2ceSHans Rosenfeld  * Verify whether or not we can support this CCID reader.
2679a61ed2ceSHans Rosenfeld  */
2680a61ed2ceSHans Rosenfeld static boolean_t
ccid_supported(ccid_t * ccid)2681a61ed2ceSHans Rosenfeld ccid_supported(ccid_t *ccid)
2682a61ed2ceSHans Rosenfeld {
2683a61ed2ceSHans Rosenfeld 	usb_client_dev_data_t *dp;
2684a61ed2ceSHans Rosenfeld 	usb_alt_if_data_t *alt;
2685a61ed2ceSHans Rosenfeld 	ccid_class_features_t feat;
2686a61ed2ceSHans Rosenfeld 	uint_t bits;
2687a61ed2ceSHans Rosenfeld 	uint16_t ver = ccid->ccid_class.ccd_bcdCCID;
2688a61ed2ceSHans Rosenfeld 
2689a61ed2ceSHans Rosenfeld 	if (CCID_VERSION_MAJOR(ver) != CCID_VERSION_ONE) {
2690a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!refusing to attach to CCID with unsupported "
2691a61ed2ceSHans Rosenfeld 		    "version %x.%2x", CCID_VERSION_MAJOR(ver),
2692a61ed2ceSHans Rosenfeld 		    CCID_VERSION_MINOR(ver));
2693a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2694a61ed2ceSHans Rosenfeld 	}
2695a61ed2ceSHans Rosenfeld 
2696a61ed2ceSHans Rosenfeld 	/*
2697a61ed2ceSHans Rosenfeld 	 * Check the number of endpoints. This should have either two or three.
2698a61ed2ceSHans Rosenfeld 	 * If three, that means we should expect an interrupt-IN endpoint.
2699a61ed2ceSHans Rosenfeld 	 * Otherwise, we shouldn't. Any other value indicates something weird
2700a61ed2ceSHans Rosenfeld 	 * that we should ignore.
2701a61ed2ceSHans Rosenfeld 	 */
2702a61ed2ceSHans Rosenfeld 	dp = ccid->ccid_dev_data;
2703a61ed2ceSHans Rosenfeld 	alt = &dp->dev_curr_cfg->cfg_if[dp->dev_curr_if].if_alt[0];
2704a61ed2ceSHans Rosenfeld 	switch (alt->altif_descr.bNumEndpoints) {
2705a61ed2ceSHans Rosenfeld 	case 2:
2706a61ed2ceSHans Rosenfeld 		ccid->ccid_flags &= ~CCID_F_HAS_INTR;
2707a61ed2ceSHans Rosenfeld 		break;
2708a61ed2ceSHans Rosenfeld 	case 3:
2709a61ed2ceSHans Rosenfeld 		ccid->ccid_flags |= CCID_F_HAS_INTR;
2710a61ed2ceSHans Rosenfeld 		break;
2711a61ed2ceSHans Rosenfeld 	default:
2712a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!refusing to attach to CCID with unsupported "
2713a61ed2ceSHans Rosenfeld 		    "number of endpoints: %d", alt->altif_descr.bNumEndpoints);
2714a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2715a61ed2ceSHans Rosenfeld 	}
2716a61ed2ceSHans Rosenfeld 
2717a61ed2ceSHans Rosenfeld 	/*
2718a61ed2ceSHans Rosenfeld 	 * Try and determine the appropriate buffer size. This can be a little
2719a61ed2ceSHans Rosenfeld 	 * tricky. The class descriptor tells us the maximum size that the
2720a61ed2ceSHans Rosenfeld 	 * reader accepts. While it may be tempting to try and use a larger
2721a61ed2ceSHans Rosenfeld 	 * value such as the maximum size, the readers really don't like
2722a61ed2ceSHans Rosenfeld 	 * receiving bulk transfers that large. However, there are also reports
2723a61ed2ceSHans Rosenfeld 	 * of readers that will overwrite to a fixed minimum size. Until we see
2724a61ed2ceSHans Rosenfeld 	 * such a thing in the wild there's probably no point in trying to deal
2725a61ed2ceSHans Rosenfeld 	 * with it here.
2726a61ed2ceSHans Rosenfeld 	 */
2727a61ed2ceSHans Rosenfeld 	ccid->ccid_bufsize = ccid->ccid_class.ccd_dwMaxCCIDMessageLength;
2728a61ed2ceSHans Rosenfeld 	if (ccid->ccid_bufsize < CCID_MIN_MESSAGE_LENGTH) {
2729a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!CCID reader maximum CCID message length (%u)"
2730a61ed2ceSHans Rosenfeld 		    " is less than minimum packet length (%u)",
2731a61ed2ceSHans Rosenfeld 		    ccid->ccid_bufsize, CCID_MIN_MESSAGE_LENGTH);
2732a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2733a61ed2ceSHans Rosenfeld 	}
2734a61ed2ceSHans Rosenfeld 
2735a61ed2ceSHans Rosenfeld 	/*
2736a61ed2ceSHans Rosenfeld 	 * At this time, we do not require that the system have automatic ICC
2737a61ed2ceSHans Rosenfeld 	 * activation or automatic ICC voltage. These are handled automatically
2738a61ed2ceSHans Rosenfeld 	 * by the system.
2739a61ed2ceSHans Rosenfeld 	 */
2740a61ed2ceSHans Rosenfeld 	feat = ccid->ccid_class.ccd_dwFeatures;
2741a61ed2ceSHans Rosenfeld 
2742a61ed2ceSHans Rosenfeld 	/*
2743a61ed2ceSHans Rosenfeld 	 * Check the number of data rates that are supported by the reader. If
2744a61ed2ceSHans Rosenfeld 	 * the reader has a non-zero value and we don't support automatic
2745a61ed2ceSHans Rosenfeld 	 * negotiation then warn about that.
2746a61ed2ceSHans Rosenfeld 	 */
2747a61ed2ceSHans Rosenfeld 	if (ccid->ccid_class.ccd_bNumDataRatesSupported != 0 &&
2748a61ed2ceSHans Rosenfeld 	    (feat & CCID_CLASS_F_AUTO_BAUD) == 0) {
2749a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!CCID reader only supports fixed clock rates,"
2750a61ed2ceSHans Rosenfeld 		    " data will be limited to default values");
2751a61ed2ceSHans Rosenfeld 	}
2752a61ed2ceSHans Rosenfeld 
2753a61ed2ceSHans Rosenfeld 	/*
2754a61ed2ceSHans Rosenfeld 	 * Check which automatic features the reader provides and which features
2755a61ed2ceSHans Rosenfeld 	 * it does not. Missing features will require additional work before a
2756a61ed2ceSHans Rosenfeld 	 * card can be activated. Note, this also applies to APDU based readers
2757a61ed2ceSHans Rosenfeld 	 * which may need to have various aspects of the device negotiated.
2758a61ed2ceSHans Rosenfeld 	 */
2759a61ed2ceSHans Rosenfeld 
2760a61ed2ceSHans Rosenfeld 	/*
2761a61ed2ceSHans Rosenfeld 	 * The footnote for these two bits in CCID r1.1.0 indicates that
2762a61ed2ceSHans Rosenfeld 	 * when neither are missing we have to do the PPS negotiation
2763a61ed2ceSHans Rosenfeld 	 * ourselves.
2764a61ed2ceSHans Rosenfeld 	 */
2765a61ed2ceSHans Rosenfeld 	bits = CCID_CLASS_F_AUTO_PARAM_NEG | CCID_CLASS_F_AUTO_PPS;
2766a61ed2ceSHans Rosenfeld 	if ((feat & bits) == 0) {
2767a61ed2ceSHans Rosenfeld 		ccid->ccid_flags |= CCID_F_NEEDS_PPS;
2768a61ed2ceSHans Rosenfeld 	}
2769a61ed2ceSHans Rosenfeld 
2770a61ed2ceSHans Rosenfeld 	if ((feat & CCID_CLASS_F_AUTO_PARAM_NEG) == 0) {
2771a61ed2ceSHans Rosenfeld 		ccid->ccid_flags |= CCID_F_NEEDS_PARAMS;
2772a61ed2ceSHans Rosenfeld 	}
2773a61ed2ceSHans Rosenfeld 
2774a61ed2ceSHans Rosenfeld 	bits = CCID_CLASS_F_AUTO_BAUD | CCID_CLASS_F_AUTO_ICC_CLOCK;
2775a61ed2ceSHans Rosenfeld 	if ((feat & bits) != bits) {
2776a61ed2ceSHans Rosenfeld 		ccid->ccid_flags |= CCID_F_NEEDS_DATAFREQ;
2777a61ed2ceSHans Rosenfeld 	}
2778a61ed2ceSHans Rosenfeld 
2779a61ed2ceSHans Rosenfeld 	return (B_TRUE);
2780a61ed2ceSHans Rosenfeld }
2781a61ed2ceSHans Rosenfeld 
2782a61ed2ceSHans Rosenfeld static boolean_t
ccid_open_pipes(ccid_t * ccid)2783a61ed2ceSHans Rosenfeld ccid_open_pipes(ccid_t *ccid)
2784a61ed2ceSHans Rosenfeld {
2785a61ed2ceSHans Rosenfeld 	int ret;
2786a61ed2ceSHans Rosenfeld 	usb_ep_data_t *ep;
2787a61ed2ceSHans Rosenfeld 	usb_client_dev_data_t *data;
2788a61ed2ceSHans Rosenfeld 	usb_pipe_policy_t policy;
2789a61ed2ceSHans Rosenfeld 
2790a61ed2ceSHans Rosenfeld 	data = ccid->ccid_dev_data;
2791a61ed2ceSHans Rosenfeld 
2792a61ed2ceSHans Rosenfeld 	/*
2793a61ed2ceSHans Rosenfeld 	 * First fill all the descriptors.
2794a61ed2ceSHans Rosenfeld 	 */
2795a61ed2ceSHans Rosenfeld 	ep = usb_lookup_ep_data(ccid->ccid_dip, data, data->dev_curr_if, 0, 0,
2796a61ed2ceSHans Rosenfeld 	    USB_EP_ATTR_BULK, USB_EP_DIR_IN);
2797a61ed2ceSHans Rosenfeld 	if (ep == NULL) {
2798a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to find CCID Bulk-IN endpoint");
2799a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2800a61ed2ceSHans Rosenfeld 	}
2801a61ed2ceSHans Rosenfeld 
2802a61ed2ceSHans Rosenfeld 	if ((ret = usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
2803a61ed2ceSHans Rosenfeld 	    ccid->ccid_dip, ep, &ccid->ccid_bulkin_xdesc)) != USB_SUCCESS) {
2804a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to fill Bulk-IN xdescr: %d", ret);
2805a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2806a61ed2ceSHans Rosenfeld 	}
2807a61ed2ceSHans Rosenfeld 
2808a61ed2ceSHans Rosenfeld 	ep = usb_lookup_ep_data(ccid->ccid_dip, data, data->dev_curr_if, 0, 0,
2809a61ed2ceSHans Rosenfeld 	    USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
2810a61ed2ceSHans Rosenfeld 	if (ep == NULL) {
2811a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to find CCID Bulk-OUT endpoint");
2812a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2813a61ed2ceSHans Rosenfeld 	}
2814a61ed2ceSHans Rosenfeld 
2815a61ed2ceSHans Rosenfeld 	if ((ret = usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
2816a61ed2ceSHans Rosenfeld 	    ccid->ccid_dip, ep, &ccid->ccid_bulkout_xdesc)) != USB_SUCCESS) {
2817a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to fill Bulk-OUT xdescr: %d", ret);
2818a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2819a61ed2ceSHans Rosenfeld 	}
2820a61ed2ceSHans Rosenfeld 
2821a61ed2ceSHans Rosenfeld 	if (ccid->ccid_flags & CCID_F_HAS_INTR) {
2822a61ed2ceSHans Rosenfeld 		ep = usb_lookup_ep_data(ccid->ccid_dip, data, data->dev_curr_if,
2823a61ed2ceSHans Rosenfeld 		    0, 0, USB_EP_ATTR_INTR, USB_EP_DIR_IN);
2824a61ed2ceSHans Rosenfeld 		if (ep == NULL) {
2825a61ed2ceSHans Rosenfeld 			ccid_error(ccid, "!failed to find CCID Intr-IN "
2826a61ed2ceSHans Rosenfeld 			    "endpoint");
2827a61ed2ceSHans Rosenfeld 			return (B_FALSE);
2828a61ed2ceSHans Rosenfeld 		}
2829a61ed2ceSHans Rosenfeld 
2830a61ed2ceSHans Rosenfeld 		if ((ret = usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
2831a61ed2ceSHans Rosenfeld 		    ccid->ccid_dip, ep, &ccid->ccid_intrin_xdesc)) !=
2832a61ed2ceSHans Rosenfeld 		    USB_SUCCESS) {
2833a61ed2ceSHans Rosenfeld 			ccid_error(ccid, "!failed to fill Intr-OUT xdescr: %d",
2834a61ed2ceSHans Rosenfeld 			    ret);
2835a61ed2ceSHans Rosenfeld 			return (B_FALSE);
2836a61ed2ceSHans Rosenfeld 		}
2837a61ed2ceSHans Rosenfeld 	}
2838a61ed2ceSHans Rosenfeld 
2839a61ed2ceSHans Rosenfeld 	/*
2840a61ed2ceSHans Rosenfeld 	 * Now open up the pipes.
2841a61ed2ceSHans Rosenfeld 	 */
2842a61ed2ceSHans Rosenfeld 	bzero(&policy, sizeof (policy));
2843a61ed2ceSHans Rosenfeld 	policy.pp_max_async_reqs = CCID_NUM_ASYNC_REQS;
2844a61ed2ceSHans Rosenfeld 
2845a61ed2ceSHans Rosenfeld 	if ((ret = usb_pipe_xopen(ccid->ccid_dip, &ccid->ccid_bulkin_xdesc,
2846a61ed2ceSHans Rosenfeld 	    &policy, USB_FLAGS_SLEEP, &ccid->ccid_bulkin_pipe)) !=
2847a61ed2ceSHans Rosenfeld 	    USB_SUCCESS) {
2848a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to open Bulk-IN pipe: %d\n", ret);
2849a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2850a61ed2ceSHans Rosenfeld 	}
2851a61ed2ceSHans Rosenfeld 
2852a61ed2ceSHans Rosenfeld 	if ((ret = usb_pipe_xopen(ccid->ccid_dip, &ccid->ccid_bulkout_xdesc,
2853a61ed2ceSHans Rosenfeld 	    &policy, USB_FLAGS_SLEEP, &ccid->ccid_bulkout_pipe)) !=
2854a61ed2ceSHans Rosenfeld 	    USB_SUCCESS) {
2855a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to open Bulk-OUT pipe: %d\n", ret);
2856a61ed2ceSHans Rosenfeld 		usb_pipe_close(ccid->ccid_dip, ccid->ccid_bulkin_pipe,
2857a61ed2ceSHans Rosenfeld 		    USB_FLAGS_SLEEP, NULL, NULL);
2858a61ed2ceSHans Rosenfeld 		ccid->ccid_bulkin_pipe = NULL;
2859a61ed2ceSHans Rosenfeld 		return (B_FALSE);
2860a61ed2ceSHans Rosenfeld 	}
2861a61ed2ceSHans Rosenfeld 
2862a61ed2ceSHans Rosenfeld 	if (ccid->ccid_flags & CCID_F_HAS_INTR) {
2863a61ed2ceSHans Rosenfeld 		if ((ret = usb_pipe_xopen(ccid->ccid_dip,
2864a61ed2ceSHans Rosenfeld 		    &ccid->ccid_intrin_xdesc, &policy, USB_FLAGS_SLEEP,
2865a61ed2ceSHans Rosenfeld 		    &ccid->ccid_intrin_pipe)) != USB_SUCCESS) {
2866a61ed2ceSHans Rosenfeld 			ccid_error(ccid, "!failed to open Intr-IN pipe: %d\n",
2867a61ed2ceSHans Rosenfeld 			    ret);
2868a61ed2ceSHans Rosenfeld 			usb_pipe_close(ccid->ccid_dip, ccid->ccid_bulkin_pipe,
2869a61ed2ceSHans Rosenfeld 			    USB_FLAGS_SLEEP, NULL, NULL);
2870a61ed2ceSHans Rosenfeld 			ccid->ccid_bulkin_pipe = NULL;
2871a61ed2ceSHans Rosenfeld 			usb_pipe_close(ccid->ccid_dip, ccid->ccid_bulkout_pipe,
2872a61ed2ceSHans Rosenfeld 			    USB_FLAGS_SLEEP, NULL, NULL);
2873a61ed2ceSHans Rosenfeld 			ccid->ccid_bulkout_pipe = NULL;
2874a61ed2ceSHans Rosenfeld 			return (B_FALSE);
2875a61ed2ceSHans Rosenfeld 		}
2876a61ed2ceSHans Rosenfeld 	}
2877a61ed2ceSHans Rosenfeld 
2878a61ed2ceSHans Rosenfeld 	ccid->ccid_control_pipe = data->dev_default_ph;
2879a61ed2ceSHans Rosenfeld 	return (B_TRUE);
2880a61ed2ceSHans Rosenfeld }
2881a61ed2ceSHans Rosenfeld 
2882a61ed2ceSHans Rosenfeld static void
ccid_slots_fini(ccid_t * ccid)2883a61ed2ceSHans Rosenfeld ccid_slots_fini(ccid_t *ccid)
2884a61ed2ceSHans Rosenfeld {
2885a61ed2ceSHans Rosenfeld 	uint_t i;
2886a61ed2ceSHans Rosenfeld 
2887a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_nslots; i++) {
2888a61ed2ceSHans Rosenfeld 		VERIFY3U(ccid->ccid_slots[i].cs_slotno, ==, i);
2889a61ed2ceSHans Rosenfeld 
2890a61ed2ceSHans Rosenfeld 		if (ccid->ccid_slots[i].cs_command != NULL) {
2891a61ed2ceSHans Rosenfeld 			ccid_command_free(ccid->ccid_slots[i].cs_command);
2892a61ed2ceSHans Rosenfeld 			ccid->ccid_slots[i].cs_command = NULL;
2893a61ed2ceSHans Rosenfeld 		}
2894a61ed2ceSHans Rosenfeld 
2895a61ed2ceSHans Rosenfeld 		cv_destroy(&ccid->ccid_slots[i].cs_io.ci_cv);
2896a61ed2ceSHans Rosenfeld 		freemsgchain(ccid->ccid_slots[i].cs_atr);
2897a61ed2ceSHans Rosenfeld 		atr_data_free(ccid->ccid_slots[i].cs_icc.icc_atr_data);
2898a61ed2ceSHans Rosenfeld 		list_destroy(&ccid->ccid_slots[i].cs_minors);
2899a61ed2ceSHans Rosenfeld 		list_destroy(&ccid->ccid_slots[i].cs_excl_waiters);
2900a61ed2ceSHans Rosenfeld 	}
2901a61ed2ceSHans Rosenfeld 
2902a61ed2ceSHans Rosenfeld 	ddi_remove_minor_node(ccid->ccid_dip, NULL);
2903a61ed2ceSHans Rosenfeld 	kmem_free(ccid->ccid_slots, sizeof (ccid_slot_t) * ccid->ccid_nslots);
2904a61ed2ceSHans Rosenfeld 	ccid->ccid_nslots = 0;
2905a61ed2ceSHans Rosenfeld 	ccid->ccid_slots = NULL;
2906a61ed2ceSHans Rosenfeld }
2907a61ed2ceSHans Rosenfeld 
2908a61ed2ceSHans Rosenfeld static boolean_t
ccid_slots_init(ccid_t * ccid)2909a61ed2ceSHans Rosenfeld ccid_slots_init(ccid_t *ccid)
2910a61ed2ceSHans Rosenfeld {
2911a61ed2ceSHans Rosenfeld 	uint_t i;
2912a61ed2ceSHans Rosenfeld 
2913a61ed2ceSHans Rosenfeld 	/*
2914a61ed2ceSHans Rosenfeld 	 * The class descriptor has the maximum index that one can index into.
2915a61ed2ceSHans Rosenfeld 	 * We therefore have to add one to determine the actual number of slots
2916a61ed2ceSHans Rosenfeld 	 * that exist.
2917a61ed2ceSHans Rosenfeld 	 */
2918a61ed2ceSHans Rosenfeld 	ccid->ccid_nslots = ccid->ccid_class.ccd_bMaxSlotIndex + 1;
2919a61ed2ceSHans Rosenfeld 	ccid->ccid_slots = kmem_zalloc(sizeof (ccid_slot_t) * ccid->ccid_nslots,
2920a61ed2ceSHans Rosenfeld 	    KM_SLEEP);
2921a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_nslots; i++) {
2922a61ed2ceSHans Rosenfeld 		ccid_slot_t *slot = &ccid->ccid_slots[i];
2923a61ed2ceSHans Rosenfeld 
2924a61ed2ceSHans Rosenfeld 		/*
2925a61ed2ceSHans Rosenfeld 		 * We initialize every possible slot as having changed to make
2926a61ed2ceSHans Rosenfeld 		 * sure that we have a chance to discover it. See the slot
2927a61ed2ceSHans Rosenfeld 		 * detection section in the big theory statement for more info.
2928a61ed2ceSHans Rosenfeld 		 */
2929a61ed2ceSHans Rosenfeld 		slot->cs_flags |= CCID_SLOT_F_CHANGED;
2930a61ed2ceSHans Rosenfeld 		slot->cs_slotno = i;
2931a61ed2ceSHans Rosenfeld 		slot->cs_ccid = ccid;
2932a61ed2ceSHans Rosenfeld 		slot->cs_icc.icc_atr_data = atr_data_alloc();
2933a61ed2ceSHans Rosenfeld 		slot->cs_idx.cmi_minor = CCID_MINOR_INVALID;
2934a61ed2ceSHans Rosenfeld 		slot->cs_idx.cmi_isslot = B_TRUE;
2935a61ed2ceSHans Rosenfeld 		slot->cs_idx.cmi_data.cmi_slot = slot;
2936a61ed2ceSHans Rosenfeld 		cv_init(&slot->cs_io.ci_cv, NULL, CV_DRIVER, NULL);
2937a61ed2ceSHans Rosenfeld 		list_create(&slot->cs_minors, sizeof (ccid_minor_t),
2938a61ed2ceSHans Rosenfeld 		    offsetof(ccid_minor_t, cm_minor_list));
2939a61ed2ceSHans Rosenfeld 		list_create(&slot->cs_excl_waiters, sizeof (ccid_minor_t),
2940a61ed2ceSHans Rosenfeld 		    offsetof(ccid_minor_t, cm_excl_list));
2941a61ed2ceSHans Rosenfeld 	}
2942a61ed2ceSHans Rosenfeld 
2943a61ed2ceSHans Rosenfeld 	return (B_TRUE);
2944a61ed2ceSHans Rosenfeld }
2945a61ed2ceSHans Rosenfeld 
2946a61ed2ceSHans Rosenfeld static void
ccid_minors_fini(ccid_t * ccid)2947a61ed2ceSHans Rosenfeld ccid_minors_fini(ccid_t *ccid)
2948a61ed2ceSHans Rosenfeld {
2949a61ed2ceSHans Rosenfeld 	uint_t i;
2950a61ed2ceSHans Rosenfeld 
2951a61ed2ceSHans Rosenfeld 	ddi_remove_minor_node(ccid->ccid_dip, NULL);
2952a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_nslots; i++) {
2953a61ed2ceSHans Rosenfeld 		if (ccid->ccid_slots[i].cs_idx.cmi_minor == CCID_MINOR_INVALID)
2954a61ed2ceSHans Rosenfeld 			continue;
2955a61ed2ceSHans Rosenfeld 		ccid_minor_idx_free(&ccid->ccid_slots[i].cs_idx);
2956a61ed2ceSHans Rosenfeld 	}
2957a61ed2ceSHans Rosenfeld }
2958a61ed2ceSHans Rosenfeld 
2959a61ed2ceSHans Rosenfeld static boolean_t
ccid_minors_init(ccid_t * ccid)2960a61ed2ceSHans Rosenfeld ccid_minors_init(ccid_t *ccid)
2961a61ed2ceSHans Rosenfeld {
2962a61ed2ceSHans Rosenfeld 	uint_t i;
2963a61ed2ceSHans Rosenfeld 
2964a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_nslots; i++) {
2965a61ed2ceSHans Rosenfeld 		char buf[32];
2966a61ed2ceSHans Rosenfeld 
2967a61ed2ceSHans Rosenfeld 		(void) ccid_minor_idx_alloc(&ccid->ccid_slots[i].cs_idx,
2968a61ed2ceSHans Rosenfeld 		    B_TRUE);
2969a61ed2ceSHans Rosenfeld 
2970a61ed2ceSHans Rosenfeld 		(void) snprintf(buf, sizeof (buf), "slot%u", i);
2971a61ed2ceSHans Rosenfeld 		if (ddi_create_minor_node(ccid->ccid_dip, buf, S_IFCHR,
2972a61ed2ceSHans Rosenfeld 		    ccid->ccid_slots[i].cs_idx.cmi_minor,
2973a61ed2ceSHans Rosenfeld 		    DDI_NT_CCID_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
2974a61ed2ceSHans Rosenfeld 			ccid_minors_fini(ccid);
2975a61ed2ceSHans Rosenfeld 			return (B_FALSE);
2976a61ed2ceSHans Rosenfeld 		}
2977a61ed2ceSHans Rosenfeld 	}
2978a61ed2ceSHans Rosenfeld 
2979a61ed2ceSHans Rosenfeld 	return (B_TRUE);
2980a61ed2ceSHans Rosenfeld }
2981a61ed2ceSHans Rosenfeld 
2982a61ed2ceSHans Rosenfeld static void
ccid_intr_poll_fini(ccid_t * ccid)2983a61ed2ceSHans Rosenfeld ccid_intr_poll_fini(ccid_t *ccid)
2984a61ed2ceSHans Rosenfeld {
2985a61ed2ceSHans Rosenfeld 	if (ccid->ccid_flags & CCID_F_HAS_INTR) {
2986a61ed2ceSHans Rosenfeld 		timeout_id_t tid;
2987a61ed2ceSHans Rosenfeld 
2988a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
2989a61ed2ceSHans Rosenfeld 		tid = ccid->ccid_poll_timeout;
2990a61ed2ceSHans Rosenfeld 		ccid->ccid_poll_timeout = NULL;
2991a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
2992a61ed2ceSHans Rosenfeld 		(void) untimeout(tid);
2993a61ed2ceSHans Rosenfeld 		usb_pipe_stop_intr_polling(ccid->ccid_intrin_pipe,
2994a61ed2ceSHans Rosenfeld 		    USB_FLAGS_SLEEP);
2995a61ed2ceSHans Rosenfeld 	} else {
2996a61ed2ceSHans Rosenfeld 		VERIFY3P(ccid->ccid_intrin_pipe, ==, NULL);
2997a61ed2ceSHans Rosenfeld 	}
2998a61ed2ceSHans Rosenfeld }
2999a61ed2ceSHans Rosenfeld 
3000a61ed2ceSHans Rosenfeld static void
ccid_intr_poll_init(ccid_t * ccid)3001a61ed2ceSHans Rosenfeld ccid_intr_poll_init(ccid_t *ccid)
3002a61ed2ceSHans Rosenfeld {
3003a61ed2ceSHans Rosenfeld 	int ret;
3004a61ed2ceSHans Rosenfeld 	usb_intr_req_t *uirp;
3005a61ed2ceSHans Rosenfeld 
3006a61ed2ceSHans Rosenfeld 	uirp = usb_alloc_intr_req(ccid->ccid_dip, 0, USB_FLAGS_SLEEP);
3007a61ed2ceSHans Rosenfeld 	uirp->intr_client_private = (usb_opaque_t)ccid;
3008a61ed2ceSHans Rosenfeld 	uirp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
3009a61ed2ceSHans Rosenfeld 	    USB_ATTRS_AUTOCLEARING;
3010a61ed2ceSHans Rosenfeld 	uirp->intr_len = CCID_INTR_RESPONSE_SIZE;
3011a61ed2ceSHans Rosenfeld 	uirp->intr_cb = ccid_intr_pipe_cb;
3012a61ed2ceSHans Rosenfeld 	uirp->intr_exc_cb = ccid_intr_pipe_except_cb;
3013a61ed2ceSHans Rosenfeld 
3014a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3015a61ed2ceSHans Rosenfeld 	if (ccid->ccid_flags & CCID_F_DEV_GONE_MASK) {
3016a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3017a61ed2ceSHans Rosenfeld 		usb_free_intr_req(uirp);
3018a61ed2ceSHans Rosenfeld 		return;
3019a61ed2ceSHans Rosenfeld 	}
3020a61ed2ceSHans Rosenfeld 
3021a61ed2ceSHans Rosenfeld 	if ((ret = usb_pipe_intr_xfer(ccid->ccid_intrin_pipe, uirp,
3022a61ed2ceSHans Rosenfeld 	    USB_FLAGS_SLEEP)) != USB_SUCCESS) {
3023a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to start polling on CCID Intr-IN "
3024a61ed2ceSHans Rosenfeld 		    "pipe: %d", ret);
3025a61ed2ceSHans Rosenfeld 		ccid->ccid_poll_timeout = timeout(ccid_intr_restart_timeout,
3026a61ed2ceSHans Rosenfeld 		    ccid, drv_usectohz(1000000));
3027a61ed2ceSHans Rosenfeld 		usb_free_intr_req(uirp);
3028a61ed2ceSHans Rosenfeld 	}
3029a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3030a61ed2ceSHans Rosenfeld }
3031a61ed2ceSHans Rosenfeld 
3032a61ed2ceSHans Rosenfeld static void
ccid_cleanup_bulkin(ccid_t * ccid)3033a61ed2ceSHans Rosenfeld ccid_cleanup_bulkin(ccid_t *ccid)
3034a61ed2ceSHans Rosenfeld {
3035a61ed2ceSHans Rosenfeld 	uint_t i;
3036a61ed2ceSHans Rosenfeld 
3037a61ed2ceSHans Rosenfeld 	VERIFY3P(ccid->ccid_bulkin_dispatched, ==, NULL);
3038a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_bulkin_alloced; i++) {
3039a61ed2ceSHans Rosenfeld 		VERIFY3P(ccid->ccid_bulkin_cache[i], !=, NULL);
3040a61ed2ceSHans Rosenfeld 		usb_free_bulk_req(ccid->ccid_bulkin_cache[i]);
3041a61ed2ceSHans Rosenfeld 		ccid->ccid_bulkin_cache[i] = NULL;
3042a61ed2ceSHans Rosenfeld 	}
3043a61ed2ceSHans Rosenfeld 
3044a61ed2ceSHans Rosenfeld #ifdef	DEBUG
3045a61ed2ceSHans Rosenfeld 	for (i = 0; i < CCID_BULK_NALLOCED; i++) {
3046a61ed2ceSHans Rosenfeld 		VERIFY3P(ccid->ccid_bulkin_cache[i], ==, NULL);
3047a61ed2ceSHans Rosenfeld 	}
3048a61ed2ceSHans Rosenfeld #endif
3049a61ed2ceSHans Rosenfeld 	ccid->ccid_bulkin_alloced = 0;
3050a61ed2ceSHans Rosenfeld }
3051a61ed2ceSHans Rosenfeld 
3052a61ed2ceSHans Rosenfeld static int
ccid_disconnect_cb(dev_info_t * dip)3053a61ed2ceSHans Rosenfeld ccid_disconnect_cb(dev_info_t *dip)
3054a61ed2ceSHans Rosenfeld {
3055a61ed2ceSHans Rosenfeld 	int inst;
3056a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
3057a61ed2ceSHans Rosenfeld 	uint_t i;
3058a61ed2ceSHans Rosenfeld 
3059a61ed2ceSHans Rosenfeld 	if (dip == NULL)
3060a61ed2ceSHans Rosenfeld 		goto done;
3061a61ed2ceSHans Rosenfeld 
3062a61ed2ceSHans Rosenfeld 	inst = ddi_get_instance(dip);
3063a61ed2ceSHans Rosenfeld 	ccid = ddi_get_soft_state(ccid_softstate, inst);
3064a61ed2ceSHans Rosenfeld 	if (ccid == NULL)
3065a61ed2ceSHans Rosenfeld 		goto done;
3066a61ed2ceSHans Rosenfeld 	VERIFY3P(dip, ==, ccid->ccid_dip);
3067a61ed2ceSHans Rosenfeld 
3068a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3069a61ed2ceSHans Rosenfeld 	/*
3070a61ed2ceSHans Rosenfeld 	 * First, set the disconnected flag. This will make sure that anyone
3071a61ed2ceSHans Rosenfeld 	 * that tries to make additional operations will be kicked out. This
3072a61ed2ceSHans Rosenfeld 	 * flag is checked by detach and by users.
3073a61ed2ceSHans Rosenfeld 	 */
3074a61ed2ceSHans Rosenfeld 	ccid->ccid_flags |= CCID_F_DISCONNECTED;
3075a61ed2ceSHans Rosenfeld 
3076a61ed2ceSHans Rosenfeld 	/*
3077a61ed2ceSHans Rosenfeld 	 * Now, go through any threads that are blocked on a minor for exclusive
3078a61ed2ceSHans Rosenfeld 	 * access. They should be woken up and they'll fail due to the fact that
3079a61ed2ceSHans Rosenfeld 	 * we've set the disconnected flag above.
3080a61ed2ceSHans Rosenfeld 	 */
3081a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_nslots; i++) {
3082a61ed2ceSHans Rosenfeld 		ccid_minor_t *cmp;
3083a61ed2ceSHans Rosenfeld 		ccid_slot_t *slot = &ccid->ccid_slots[i];
3084a61ed2ceSHans Rosenfeld 
3085a61ed2ceSHans Rosenfeld 		for (cmp = list_head(&slot->cs_excl_waiters); cmp != NULL;
3086a61ed2ceSHans Rosenfeld 		    cmp = list_next(&slot->cs_excl_waiters, cmp)) {
3087a61ed2ceSHans Rosenfeld 			cv_signal(&cmp->cm_excl_cv);
3088a61ed2ceSHans Rosenfeld 		}
3089a61ed2ceSHans Rosenfeld 	}
3090a61ed2ceSHans Rosenfeld 
3091a61ed2ceSHans Rosenfeld 	/*
3092a61ed2ceSHans Rosenfeld 	 * Finally, we need to basically wake up anyone blocked in read and make
3093a61ed2ceSHans Rosenfeld 	 * sure that they don't wait there forever and make sure that anyone
3094a61ed2ceSHans Rosenfeld 	 * polling gets a POLLHUP. We can't really distinguish between this and
3095a61ed2ceSHans Rosenfeld 	 * an ICC being removed. It will be discovered when someone tries to do
3096a61ed2ceSHans Rosenfeld 	 * an operation and they receive an ENODEV. We only need to do this on
3097a61ed2ceSHans Rosenfeld 	 * minors that have exclusive access. Don't worry about them finishing
3098a61ed2ceSHans Rosenfeld 	 * up, this'll be done as part of detach.
3099a61ed2ceSHans Rosenfeld 	 */
3100a61ed2ceSHans Rosenfeld 	for (i = 0; i < ccid->ccid_nslots; i++) {
3101a61ed2ceSHans Rosenfeld 		ccid_slot_t *slot = &ccid->ccid_slots[i];
3102a61ed2ceSHans Rosenfeld 		if (slot->cs_excl_minor == NULL)
3103a61ed2ceSHans Rosenfeld 			continue;
3104a61ed2ceSHans Rosenfeld 
3105a61ed2ceSHans Rosenfeld 		pollwakeup(&slot->cs_excl_minor->cm_pollhead,
3106a61ed2ceSHans Rosenfeld 		    POLLHUP | POLLERR);
3107a61ed2ceSHans Rosenfeld 		cv_signal(&slot->cs_excl_minor->cm_read_cv);
3108a61ed2ceSHans Rosenfeld 	}
3109a61ed2ceSHans Rosenfeld 
3110a61ed2ceSHans Rosenfeld 	/*
3111a61ed2ceSHans Rosenfeld 	 * If there are outstanding commands, they will ultimately be cleaned
3112a61ed2ceSHans Rosenfeld 	 * up as the USB commands themselves time out. We will get notified
3113a61ed2ceSHans Rosenfeld 	 * through the various bulk xfer exception callbacks, which will induce
3114a61ed2ceSHans Rosenfeld 	 * the cleanup through ccid_command_transport_error(). This will also
3115a61ed2ceSHans Rosenfeld 	 * take care of commands waiting for I/O teardown.
3116a61ed2ceSHans Rosenfeld 	 */
3117a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3118a61ed2ceSHans Rosenfeld 
3119a61ed2ceSHans Rosenfeld done:
3120a61ed2ceSHans Rosenfeld 	return (USB_SUCCESS);
3121a61ed2ceSHans Rosenfeld }
3122a61ed2ceSHans Rosenfeld 
3123a61ed2ceSHans Rosenfeld static usb_event_t ccid_usb_events = {
3124a61ed2ceSHans Rosenfeld 	ccid_disconnect_cb,
3125a61ed2ceSHans Rosenfeld 	NULL,
3126a61ed2ceSHans Rosenfeld 	NULL,
3127a61ed2ceSHans Rosenfeld 	NULL
3128a61ed2ceSHans Rosenfeld };
3129a61ed2ceSHans Rosenfeld 
3130a61ed2ceSHans Rosenfeld static void
ccid_cleanup(dev_info_t * dip)3131a61ed2ceSHans Rosenfeld ccid_cleanup(dev_info_t *dip)
3132a61ed2ceSHans Rosenfeld {
3133a61ed2ceSHans Rosenfeld 	int inst;
3134a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
3135a61ed2ceSHans Rosenfeld 
3136a61ed2ceSHans Rosenfeld 	if (dip == NULL)
3137a61ed2ceSHans Rosenfeld 		return;
3138a61ed2ceSHans Rosenfeld 
3139a61ed2ceSHans Rosenfeld 	inst = ddi_get_instance(dip);
3140a61ed2ceSHans Rosenfeld 	ccid = ddi_get_soft_state(ccid_softstate, inst);
3141a61ed2ceSHans Rosenfeld 	if (ccid == NULL)
3142a61ed2ceSHans Rosenfeld 		return;
3143a61ed2ceSHans Rosenfeld 	VERIFY3P(dip, ==, ccid->ccid_dip);
3144a61ed2ceSHans Rosenfeld 
3145a61ed2ceSHans Rosenfeld 	/*
3146a61ed2ceSHans Rosenfeld 	 * Make sure we set the detaching flag so anything running in the
3147a61ed2ceSHans Rosenfeld 	 * background knows to stop.
3148a61ed2ceSHans Rosenfeld 	 */
3149a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3150a61ed2ceSHans Rosenfeld 	ccid->ccid_flags |= CCID_F_DETACHING;
3151a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3152a61ed2ceSHans Rosenfeld 
3153a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_MINORS) != 0) {
3154a61ed2ceSHans Rosenfeld 		ccid_minors_fini(ccid);
3155a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_MINORS;
3156a61ed2ceSHans Rosenfeld 	}
3157a61ed2ceSHans Rosenfeld 
3158a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_INTR_ACTIVE) != 0) {
3159a61ed2ceSHans Rosenfeld 		ccid_intr_poll_fini(ccid);
3160a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_INTR_ACTIVE;
3161a61ed2ceSHans Rosenfeld 	}
3162a61ed2ceSHans Rosenfeld 
3163a61ed2ceSHans Rosenfeld 	/*
3164a61ed2ceSHans Rosenfeld 	 * At this point, we have shut down the interrupt pipe, the last place
3165a61ed2ceSHans Rosenfeld 	 * aside from a user that could have kicked off I/O. So finally wait for
3166a61ed2ceSHans Rosenfeld 	 * any worker threads.
3167a61ed2ceSHans Rosenfeld 	 */
3168a61ed2ceSHans Rosenfeld 	if (ccid->ccid_taskq != NULL) {
3169a61ed2ceSHans Rosenfeld 		ddi_taskq_wait(ccid->ccid_taskq);
3170a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
3171a61ed2ceSHans Rosenfeld 		VERIFY0(ccid->ccid_flags & CCID_F_WORKER_MASK);
3172a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3173a61ed2ceSHans Rosenfeld 	}
3174a61ed2ceSHans Rosenfeld 
3175a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_HOTPLUG_CB) != 0) {
3176a61ed2ceSHans Rosenfeld 		usb_unregister_event_cbs(dip, &ccid_usb_events);
3177a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_HOTPLUG_CB;
3178a61ed2ceSHans Rosenfeld 	}
3179a61ed2ceSHans Rosenfeld 
3180a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_SLOTS) != 0) {
3181a61ed2ceSHans Rosenfeld 		ccid_slots_fini(ccid);
3182a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_SLOTS;
3183a61ed2ceSHans Rosenfeld 	}
3184a61ed2ceSHans Rosenfeld 
3185a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_SEQ_IDS) != 0) {
3186a61ed2ceSHans Rosenfeld 		id_space_destroy(ccid->ccid_seqs);
3187a61ed2ceSHans Rosenfeld 		ccid->ccid_seqs = NULL;
3188a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_SEQ_IDS;
3189a61ed2ceSHans Rosenfeld 	}
3190a61ed2ceSHans Rosenfeld 
3191a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_OPEN_PIPES) != 0) {
3192a61ed2ceSHans Rosenfeld 		usb_pipe_close(dip, ccid->ccid_bulkin_pipe, USB_FLAGS_SLEEP,
3193a61ed2ceSHans Rosenfeld 		    NULL, NULL);
3194a61ed2ceSHans Rosenfeld 		ccid->ccid_bulkin_pipe = NULL;
3195a61ed2ceSHans Rosenfeld 		usb_pipe_close(dip, ccid->ccid_bulkout_pipe, USB_FLAGS_SLEEP,
3196a61ed2ceSHans Rosenfeld 		    NULL, NULL);
3197a61ed2ceSHans Rosenfeld 		ccid->ccid_bulkout_pipe = NULL;
3198a61ed2ceSHans Rosenfeld 		if ((ccid->ccid_flags & CCID_F_HAS_INTR) != 0) {
3199a61ed2ceSHans Rosenfeld 			usb_pipe_close(dip, ccid->ccid_intrin_pipe,
3200a61ed2ceSHans Rosenfeld 			    USB_FLAGS_SLEEP, NULL, NULL);
3201a61ed2ceSHans Rosenfeld 			ccid->ccid_intrin_pipe = NULL;
3202a61ed2ceSHans Rosenfeld 		} else {
3203a61ed2ceSHans Rosenfeld 			VERIFY3P(ccid->ccid_intrin_pipe, ==, NULL);
3204a61ed2ceSHans Rosenfeld 		}
3205a61ed2ceSHans Rosenfeld 		ccid->ccid_control_pipe = NULL;
3206a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_OPEN_PIPES;
3207a61ed2ceSHans Rosenfeld 	}
3208a61ed2ceSHans Rosenfeld 
3209a61ed2ceSHans Rosenfeld 	/*
3210a61ed2ceSHans Rosenfeld 	 * Now that all of the pipes are closed. If we happened to have any
3211a61ed2ceSHans Rosenfeld 	 * cached bulk requests, we should free them.
3212a61ed2ceSHans Rosenfeld 	 */
3213a61ed2ceSHans Rosenfeld 	ccid_cleanup_bulkin(ccid);
3214a61ed2ceSHans Rosenfeld 
3215a61ed2ceSHans Rosenfeld 	if (ccid->ccid_attach & CCID_ATTACH_CMD_LIST) {
3216a61ed2ceSHans Rosenfeld 		ccid_command_t *cc;
3217a61ed2ceSHans Rosenfeld 
3218a61ed2ceSHans Rosenfeld 		while ((cc = list_remove_head(&ccid->ccid_command_queue)) !=
3219a61ed2ceSHans Rosenfeld 		    NULL) {
3220a61ed2ceSHans Rosenfeld 			ccid_command_free(cc);
3221a61ed2ceSHans Rosenfeld 		}
3222a61ed2ceSHans Rosenfeld 		list_destroy(&ccid->ccid_command_queue);
3223a61ed2ceSHans Rosenfeld 
3224a61ed2ceSHans Rosenfeld 		while ((cc = list_remove_head(&ccid->ccid_complete_queue)) !=
3225a61ed2ceSHans Rosenfeld 		    NULL) {
3226a61ed2ceSHans Rosenfeld 			ccid_command_free(cc);
3227a61ed2ceSHans Rosenfeld 		}
3228a61ed2ceSHans Rosenfeld 		list_destroy(&ccid->ccid_complete_queue);
3229a61ed2ceSHans Rosenfeld 	}
3230a61ed2ceSHans Rosenfeld 
3231a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_TASKQ) != 0) {
3232a61ed2ceSHans Rosenfeld 		ddi_taskq_destroy(ccid->ccid_taskq);
3233a61ed2ceSHans Rosenfeld 		ccid->ccid_taskq = NULL;
3234a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_TASKQ;
3235a61ed2ceSHans Rosenfeld 	}
3236a61ed2ceSHans Rosenfeld 
3237a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_MUTEX_INIT) != 0) {
3238a61ed2ceSHans Rosenfeld 		mutex_destroy(&ccid->ccid_mutex);
3239a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_MUTEX_INIT;
3240a61ed2ceSHans Rosenfeld 	}
3241a61ed2ceSHans Rosenfeld 
3242a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_attach & CCID_ATTACH_USB_CLIENT) != 0) {
3243a61ed2ceSHans Rosenfeld 		usb_client_detach(dip, ccid->ccid_dev_data);
3244a61ed2ceSHans Rosenfeld 		ccid->ccid_dev_data = NULL;
3245a61ed2ceSHans Rosenfeld 		ccid->ccid_attach &= ~CCID_ATTACH_USB_CLIENT;
3246a61ed2ceSHans Rosenfeld 	}
3247a61ed2ceSHans Rosenfeld 
3248a61ed2ceSHans Rosenfeld 	ASSERT0(ccid->ccid_attach);
3249a61ed2ceSHans Rosenfeld 	ddi_soft_state_free(ccid_softstate, inst);
3250a61ed2ceSHans Rosenfeld }
3251a61ed2ceSHans Rosenfeld 
3252a61ed2ceSHans Rosenfeld static int
ccid_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3253a61ed2ceSHans Rosenfeld ccid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3254a61ed2ceSHans Rosenfeld {
3255a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
3256a61ed2ceSHans Rosenfeld 	int inst, ret;
3257a61ed2ceSHans Rosenfeld 	char buf[64];
3258a61ed2ceSHans Rosenfeld 
3259a61ed2ceSHans Rosenfeld 	if (cmd != DDI_ATTACH)
3260a61ed2ceSHans Rosenfeld 		return (DDI_FAILURE);
3261a61ed2ceSHans Rosenfeld 
3262a61ed2ceSHans Rosenfeld 	inst = ddi_get_instance(dip);
3263a61ed2ceSHans Rosenfeld 	if (ddi_soft_state_zalloc(ccid_softstate, inst) != DDI_SUCCESS) {
3264a61ed2ceSHans Rosenfeld 		ccid_error(NULL, "!failed to allocate soft state for ccid "
3265a61ed2ceSHans Rosenfeld 		    "instance %d", inst);
3266a61ed2ceSHans Rosenfeld 		return (DDI_FAILURE);
3267a61ed2ceSHans Rosenfeld 	}
3268a61ed2ceSHans Rosenfeld 
3269a61ed2ceSHans Rosenfeld 	ccid = ddi_get_soft_state(ccid_softstate, inst);
3270a61ed2ceSHans Rosenfeld 	ccid->ccid_dip = dip;
3271a61ed2ceSHans Rosenfeld 
3272a61ed2ceSHans Rosenfeld 	if ((ret = usb_client_attach(dip, USBDRV_VERSION, 0)) != USB_SUCCESS) {
3273a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to attach to usb client: %d", ret);
3274a61ed2ceSHans Rosenfeld 		goto cleanup;
3275a61ed2ceSHans Rosenfeld 	}
3276a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_USB_CLIENT;
3277a61ed2ceSHans Rosenfeld 
3278a61ed2ceSHans Rosenfeld 	if ((ret = usb_get_dev_data(dip, &ccid->ccid_dev_data, USB_PARSE_LVL_IF,
3279a61ed2ceSHans Rosenfeld 	    0)) != USB_SUCCESS) {
3280a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to get usb device data: %d", ret);
3281a61ed2ceSHans Rosenfeld 		goto cleanup;
3282a61ed2ceSHans Rosenfeld 	}
3283a61ed2ceSHans Rosenfeld 
3284a61ed2ceSHans Rosenfeld 	mutex_init(&ccid->ccid_mutex, NULL, MUTEX_DRIVER,
3285a61ed2ceSHans Rosenfeld 	    ccid->ccid_dev_data->dev_iblock_cookie);
3286a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_MUTEX_INIT;
3287a61ed2ceSHans Rosenfeld 
3288a61ed2ceSHans Rosenfeld 	(void) snprintf(buf, sizeof (buf), "ccid%d_taskq", inst);
3289a61ed2ceSHans Rosenfeld 	ccid->ccid_taskq = ddi_taskq_create(dip, buf, 1, TASKQ_DEFAULTPRI, 0);
3290a61ed2ceSHans Rosenfeld 	if (ccid->ccid_taskq == NULL) {
3291a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to create CCID taskq");
3292a61ed2ceSHans Rosenfeld 		goto cleanup;
3293a61ed2ceSHans Rosenfeld 	}
3294a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_TASKQ;
3295a61ed2ceSHans Rosenfeld 
3296a61ed2ceSHans Rosenfeld 	list_create(&ccid->ccid_command_queue, sizeof (ccid_command_t),
3297a61ed2ceSHans Rosenfeld 	    offsetof(ccid_command_t, cc_list_node));
3298a61ed2ceSHans Rosenfeld 	list_create(&ccid->ccid_complete_queue, sizeof (ccid_command_t),
3299a61ed2ceSHans Rosenfeld 	    offsetof(ccid_command_t, cc_list_node));
3300a61ed2ceSHans Rosenfeld 
3301a61ed2ceSHans Rosenfeld 	if (!ccid_parse_class_desc(ccid)) {
3302a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to parse CCID class descriptor");
3303a61ed2ceSHans Rosenfeld 		goto cleanup;
3304a61ed2ceSHans Rosenfeld 	}
3305a61ed2ceSHans Rosenfeld 
3306a61ed2ceSHans Rosenfeld 	if (!ccid_supported(ccid)) {
3307a61ed2ceSHans Rosenfeld 		ccid_error(ccid,
3308a61ed2ceSHans Rosenfeld 		    "!CCID reader is not supported, not attaching");
3309a61ed2ceSHans Rosenfeld 		goto cleanup;
3310a61ed2ceSHans Rosenfeld 	}
3311a61ed2ceSHans Rosenfeld 
3312a61ed2ceSHans Rosenfeld 	if (!ccid_open_pipes(ccid)) {
3313a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to open CCID pipes, not attaching");
3314a61ed2ceSHans Rosenfeld 		goto cleanup;
3315a61ed2ceSHans Rosenfeld 	}
3316a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_OPEN_PIPES;
3317a61ed2ceSHans Rosenfeld 
3318a61ed2ceSHans Rosenfeld 	(void) snprintf(buf, sizeof (buf), "ccid%d_seqs", inst);
3319a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_seqs = id_space_create(buf, CCID_SEQ_MIN,
3320a61ed2ceSHans Rosenfeld 	    CCID_SEQ_MAX + 1)) == NULL) {
3321a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to create CCID sequence id space");
3322a61ed2ceSHans Rosenfeld 		goto cleanup;
3323a61ed2ceSHans Rosenfeld 	}
3324a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_SEQ_IDS;
3325a61ed2ceSHans Rosenfeld 
3326a61ed2ceSHans Rosenfeld 	if (!ccid_slots_init(ccid)) {
3327a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to initialize CCID slot structures");
3328a61ed2ceSHans Rosenfeld 		goto cleanup;
3329a61ed2ceSHans Rosenfeld 	}
3330a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_SLOTS;
3331a61ed2ceSHans Rosenfeld 
3332a61ed2ceSHans Rosenfeld 	if (usb_register_event_cbs(dip, &ccid_usb_events, 0) != USB_SUCCESS) {
3333a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to register USB hotplug callbacks");
3334a61ed2ceSHans Rosenfeld 		goto cleanup;
3335a61ed2ceSHans Rosenfeld 	}
3336a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_HOTPLUG_CB;
3337a61ed2ceSHans Rosenfeld 
3338a61ed2ceSHans Rosenfeld 	/*
3339a61ed2ceSHans Rosenfeld 	 * Before we enable the interrupt pipe, take a shot at priming our
3340a61ed2ceSHans Rosenfeld 	 * bulkin_cache.
3341a61ed2ceSHans Rosenfeld 	 */
3342a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3343a61ed2ceSHans Rosenfeld 	ccid_bulkin_cache_refresh(ccid);
3344a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3345a61ed2ceSHans Rosenfeld 
3346a61ed2ceSHans Rosenfeld 	if (ccid->ccid_flags & CCID_F_HAS_INTR) {
3347a61ed2ceSHans Rosenfeld 		ccid_intr_poll_init(ccid);
3348a61ed2ceSHans Rosenfeld 	}
3349a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_INTR_ACTIVE;
3350a61ed2ceSHans Rosenfeld 
3351a61ed2ceSHans Rosenfeld 	/*
3352a61ed2ceSHans Rosenfeld 	 * Create minor nodes for each slot.
3353a61ed2ceSHans Rosenfeld 	 */
3354a61ed2ceSHans Rosenfeld 	if (!ccid_minors_init(ccid)) {
3355a61ed2ceSHans Rosenfeld 		ccid_error(ccid, "!failed to create minor nodes");
3356a61ed2ceSHans Rosenfeld 		goto cleanup;
3357a61ed2ceSHans Rosenfeld 	}
3358a61ed2ceSHans Rosenfeld 	ccid->ccid_attach |= CCID_ATTACH_MINORS;
3359a61ed2ceSHans Rosenfeld 
3360a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3361a61ed2ceSHans Rosenfeld 	ccid_worker_request(ccid);
3362a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3363a61ed2ceSHans Rosenfeld 
3364a61ed2ceSHans Rosenfeld 	return (DDI_SUCCESS);
3365a61ed2ceSHans Rosenfeld 
3366a61ed2ceSHans Rosenfeld cleanup:
3367a61ed2ceSHans Rosenfeld 	ccid_cleanup(dip);
3368a61ed2ceSHans Rosenfeld 	return (DDI_FAILURE);
3369a61ed2ceSHans Rosenfeld }
3370a61ed2ceSHans Rosenfeld 
3371a61ed2ceSHans Rosenfeld static int
ccid_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** outp)3372a61ed2ceSHans Rosenfeld ccid_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **outp)
3373a61ed2ceSHans Rosenfeld {
3374a61ed2ceSHans Rosenfeld 	return (DDI_FAILURE);
3375a61ed2ceSHans Rosenfeld }
3376a61ed2ceSHans Rosenfeld 
3377a61ed2ceSHans Rosenfeld static int
ccid_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3378a61ed2ceSHans Rosenfeld ccid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3379a61ed2ceSHans Rosenfeld {
3380a61ed2ceSHans Rosenfeld 	int inst;
3381a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
3382a61ed2ceSHans Rosenfeld 
3383a61ed2ceSHans Rosenfeld 	if (cmd != DDI_DETACH)
3384a61ed2ceSHans Rosenfeld 		return (DDI_FAILURE);
3385a61ed2ceSHans Rosenfeld 
3386a61ed2ceSHans Rosenfeld 	inst = ddi_get_instance(dip);
3387a61ed2ceSHans Rosenfeld 	ccid = ddi_get_soft_state(ccid_softstate, inst);
3388a61ed2ceSHans Rosenfeld 	VERIFY3P(ccid, !=, NULL);
3389a61ed2ceSHans Rosenfeld 	VERIFY3P(dip, ==, ccid->ccid_dip);
3390a61ed2ceSHans Rosenfeld 
3391a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3392a61ed2ceSHans Rosenfeld 
3393a61ed2ceSHans Rosenfeld 	/*
3394a61ed2ceSHans Rosenfeld 	 * If the device hasn't been disconnected from a USB sense, refuse to
3395a61ed2ceSHans Rosenfeld 	 * detach. Otherwise, there's no way to guarantee that the ccid
3396a61ed2ceSHans Rosenfeld 	 * driver will be attached when a user hotplugs an ICC.
3397a61ed2ceSHans Rosenfeld 	 */
3398a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DISCONNECTED) == 0) {
3399a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3400a61ed2ceSHans Rosenfeld 		return (DDI_FAILURE);
3401a61ed2ceSHans Rosenfeld 	}
3402a61ed2ceSHans Rosenfeld 
3403a61ed2ceSHans Rosenfeld 	if (!list_is_empty(&ccid->ccid_command_queue) ||
3404a61ed2ceSHans Rosenfeld 	    !list_is_empty(&ccid->ccid_complete_queue)) {
3405a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3406a61ed2ceSHans Rosenfeld 		return (DDI_FAILURE);
3407a61ed2ceSHans Rosenfeld 	}
3408a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3409a61ed2ceSHans Rosenfeld 
3410a61ed2ceSHans Rosenfeld 	ccid_cleanup(dip);
3411a61ed2ceSHans Rosenfeld 	return (DDI_SUCCESS);
3412a61ed2ceSHans Rosenfeld }
3413a61ed2ceSHans Rosenfeld 
3414a61ed2ceSHans Rosenfeld static void
ccid_minor_free(ccid_minor_t * cmp)3415a61ed2ceSHans Rosenfeld ccid_minor_free(ccid_minor_t *cmp)
3416a61ed2ceSHans Rosenfeld {
3417a61ed2ceSHans Rosenfeld 	VERIFY3U(cmp->cm_idx.cmi_minor, ==, CCID_MINOR_INVALID);
3418a61ed2ceSHans Rosenfeld 	crfree(cmp->cm_opener);
3419a61ed2ceSHans Rosenfeld 	cv_destroy(&cmp->cm_iowait_cv);
3420a61ed2ceSHans Rosenfeld 	cv_destroy(&cmp->cm_read_cv);
3421a61ed2ceSHans Rosenfeld 	cv_destroy(&cmp->cm_excl_cv);
3422a61ed2ceSHans Rosenfeld 	kmem_free(cmp, sizeof (ccid_minor_t));
3423a61ed2ceSHans Rosenfeld 
3424a61ed2ceSHans Rosenfeld }
3425a61ed2ceSHans Rosenfeld 
3426a61ed2ceSHans Rosenfeld static int
ccid_open(dev_t * devp,int flag,int otyp,cred_t * credp)3427a61ed2ceSHans Rosenfeld ccid_open(dev_t *devp, int flag, int otyp, cred_t *credp)
3428a61ed2ceSHans Rosenfeld {
3429a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *idx;
3430a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
3431a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
3432a61ed2ceSHans Rosenfeld 
3433a61ed2ceSHans Rosenfeld 	/*
3434a61ed2ceSHans Rosenfeld 	 * Always check the zone first, to make sure we lie about it existing.
3435a61ed2ceSHans Rosenfeld 	 */
3436a61ed2ceSHans Rosenfeld 	if (crgetzoneid(credp) != GLOBAL_ZONEID)
3437a61ed2ceSHans Rosenfeld 		return (ENOENT);
3438a61ed2ceSHans Rosenfeld 
3439a61ed2ceSHans Rosenfeld 	if ((otyp & (FNDELAY | FEXCL)) != 0)
3440a61ed2ceSHans Rosenfeld 		return (EINVAL);
3441a61ed2ceSHans Rosenfeld 
3442a61ed2ceSHans Rosenfeld 	if (drv_priv(credp) != 0)
3443a61ed2ceSHans Rosenfeld 		return (EPERM);
3444a61ed2ceSHans Rosenfeld 
3445a61ed2ceSHans Rosenfeld 	if (otyp != OTYP_CHR)
3446a61ed2ceSHans Rosenfeld 		return (ENOTSUP);
3447a61ed2ceSHans Rosenfeld 
3448a61ed2ceSHans Rosenfeld 	if ((flag & FREAD) != FREAD)
3449a61ed2ceSHans Rosenfeld 		return (EINVAL);
3450a61ed2ceSHans Rosenfeld 
3451a61ed2ceSHans Rosenfeld 	idx = ccid_minor_find(getminor(*devp));
3452a61ed2ceSHans Rosenfeld 	if (idx == NULL) {
3453a61ed2ceSHans Rosenfeld 		return (ENOENT);
3454a61ed2ceSHans Rosenfeld 	}
3455a61ed2ceSHans Rosenfeld 
3456a61ed2ceSHans Rosenfeld 	/*
3457a61ed2ceSHans Rosenfeld 	 * We don't expect anyone to be able to get a non-slot related minor. If
3458a61ed2ceSHans Rosenfeld 	 * that somehow happens, guard against it and error out.
3459a61ed2ceSHans Rosenfeld 	 */
3460a61ed2ceSHans Rosenfeld 	if (!idx->cmi_isslot) {
3461a61ed2ceSHans Rosenfeld 		return (ENOENT);
3462a61ed2ceSHans Rosenfeld 	}
3463a61ed2ceSHans Rosenfeld 
3464a61ed2ceSHans Rosenfeld 	slot = idx->cmi_data.cmi_slot;
3465a61ed2ceSHans Rosenfeld 
3466a61ed2ceSHans Rosenfeld 	mutex_enter(&slot->cs_ccid->ccid_mutex);
3467a61ed2ceSHans Rosenfeld 	if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
3468a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
3469a61ed2ceSHans Rosenfeld 		return (ENODEV);
3470a61ed2ceSHans Rosenfeld 	}
3471a61ed2ceSHans Rosenfeld 	mutex_exit(&slot->cs_ccid->ccid_mutex);
3472a61ed2ceSHans Rosenfeld 
3473a61ed2ceSHans Rosenfeld 	cmp = kmem_zalloc(sizeof (ccid_minor_t), KM_SLEEP);
3474a61ed2ceSHans Rosenfeld 
3475a61ed2ceSHans Rosenfeld 	cmp->cm_idx.cmi_minor = CCID_MINOR_INVALID;
3476a61ed2ceSHans Rosenfeld 	cmp->cm_idx.cmi_isslot = B_FALSE;
3477a61ed2ceSHans Rosenfeld 	cmp->cm_idx.cmi_data.cmi_user = cmp;
3478a61ed2ceSHans Rosenfeld 	if (!ccid_minor_idx_alloc(&cmp->cm_idx, B_FALSE)) {
3479a61ed2ceSHans Rosenfeld 		kmem_free(cmp, sizeof (ccid_minor_t));
3480a61ed2ceSHans Rosenfeld 		return (ENOSPC);
3481a61ed2ceSHans Rosenfeld 	}
3482a61ed2ceSHans Rosenfeld 	cv_init(&cmp->cm_excl_cv, NULL, CV_DRIVER, NULL);
3483a61ed2ceSHans Rosenfeld 	cv_init(&cmp->cm_read_cv, NULL, CV_DRIVER, NULL);
3484a61ed2ceSHans Rosenfeld 	cv_init(&cmp->cm_iowait_cv, NULL, CV_DRIVER, NULL);
3485a61ed2ceSHans Rosenfeld 	cmp->cm_opener = crdup(credp);
3486a61ed2ceSHans Rosenfeld 	cmp->cm_slot = slot;
3487a61ed2ceSHans Rosenfeld 	*devp = makedevice(getmajor(*devp), cmp->cm_idx.cmi_minor);
3488a61ed2ceSHans Rosenfeld 
3489a61ed2ceSHans Rosenfeld 	if ((flag & FWRITE) == FWRITE) {
3490a61ed2ceSHans Rosenfeld 		cmp->cm_flags |= CCID_MINOR_F_WRITABLE;
3491a61ed2ceSHans Rosenfeld 	}
3492a61ed2ceSHans Rosenfeld 
3493a61ed2ceSHans Rosenfeld 	mutex_enter(&slot->cs_ccid->ccid_mutex);
3494a61ed2ceSHans Rosenfeld 	list_insert_tail(&slot->cs_minors, cmp);
3495a61ed2ceSHans Rosenfeld 	mutex_exit(&slot->cs_ccid->ccid_mutex);
3496a61ed2ceSHans Rosenfeld 
3497a61ed2ceSHans Rosenfeld 	return (0);
3498a61ed2ceSHans Rosenfeld }
3499a61ed2ceSHans Rosenfeld 
3500a61ed2ceSHans Rosenfeld /*
3501a61ed2ceSHans Rosenfeld  * Copy a command which may have a message block chain out to the user.
3502a61ed2ceSHans Rosenfeld  */
3503a61ed2ceSHans Rosenfeld static int
ccid_read_copyout(struct uio * uiop,const mblk_t * mp)3504a61ed2ceSHans Rosenfeld ccid_read_copyout(struct uio *uiop, const mblk_t *mp)
3505a61ed2ceSHans Rosenfeld {
3506a61ed2ceSHans Rosenfeld 	offset_t off;
3507a61ed2ceSHans Rosenfeld 
3508a61ed2ceSHans Rosenfeld 	off = uiop->uio_loffset;
3509a61ed2ceSHans Rosenfeld 	VERIFY3P(mp->b_next, ==, NULL);
3510a61ed2ceSHans Rosenfeld 
3511a61ed2ceSHans Rosenfeld 	for (; mp != NULL; mp = mp->b_cont) {
3512a61ed2ceSHans Rosenfeld 		int ret;
3513a61ed2ceSHans Rosenfeld 
3514a61ed2ceSHans Rosenfeld 		if (MBLKL(mp) == 0)
3515a61ed2ceSHans Rosenfeld 			continue;
3516a61ed2ceSHans Rosenfeld 
3517a61ed2ceSHans Rosenfeld 		ret = uiomove(mp->b_rptr, MBLKL(mp), UIO_READ, uiop);
3518a61ed2ceSHans Rosenfeld 		if (ret != 0) {
3519a61ed2ceSHans Rosenfeld 			return (EFAULT);
3520a61ed2ceSHans Rosenfeld 		}
3521a61ed2ceSHans Rosenfeld 	}
3522a61ed2ceSHans Rosenfeld 
3523a61ed2ceSHans Rosenfeld 	uiop->uio_loffset = off;
3524a61ed2ceSHans Rosenfeld 	return (0);
3525a61ed2ceSHans Rosenfeld }
3526a61ed2ceSHans Rosenfeld 
3527a61ed2ceSHans Rosenfeld /*
3528a61ed2ceSHans Rosenfeld  * Called to indicate that we are ready for a user to consume the I/O.
3529a61ed2ceSHans Rosenfeld  */
3530a61ed2ceSHans Rosenfeld static void
ccid_user_io_done(ccid_t * ccid,ccid_slot_t * slot)3531a61ed2ceSHans Rosenfeld ccid_user_io_done(ccid_t *ccid, ccid_slot_t *slot)
3532a61ed2ceSHans Rosenfeld {
3533a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
3534a61ed2ceSHans Rosenfeld 
3535a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
3536a61ed2ceSHans Rosenfeld 
3537a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_flags &= ~CCID_IO_F_IN_PROGRESS;
3538a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_flags |= CCID_IO_F_DONE;
3539a61ed2ceSHans Rosenfeld 	cmp = slot->cs_excl_minor;
3540a61ed2ceSHans Rosenfeld 	if (cmp != NULL) {
3541a61ed2ceSHans Rosenfeld 		ccid_slot_pollin_signal(slot);
3542a61ed2ceSHans Rosenfeld 		cv_signal(&cmp->cm_read_cv);
3543a61ed2ceSHans Rosenfeld 	}
3544a61ed2ceSHans Rosenfeld }
3545a61ed2ceSHans Rosenfeld 
3546a61ed2ceSHans Rosenfeld /*
3547a61ed2ceSHans Rosenfeld  * This is called in a few different sitautions. It's called when an exclusive
3548a61ed2ceSHans Rosenfeld  * hold is being released by a user on the slot. It's also called when the ICC
3549a61ed2ceSHans Rosenfeld  * is removed, the reader has been unplugged, or the ICC is being reset. In all
3550a61ed2ceSHans Rosenfeld  * these cases we need to make sure that I/O is taken care of and we won't be
3551a61ed2ceSHans Rosenfeld  * leaving behind vestigial garbage.
3552a61ed2ceSHans Rosenfeld  */
3553a61ed2ceSHans Rosenfeld static void
ccid_teardown_apdu(ccid_t * ccid,ccid_slot_t * slot,int error)3554a61ed2ceSHans Rosenfeld ccid_teardown_apdu(ccid_t *ccid, ccid_slot_t *slot, int error)
3555a61ed2ceSHans Rosenfeld {
3556a61ed2ceSHans Rosenfeld 
3557a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
3558a61ed2ceSHans Rosenfeld 
3559a61ed2ceSHans Rosenfeld 	/*
3560a61ed2ceSHans Rosenfeld 	 * If no I/O is in progress, then there's nothing to do at our end.
3561a61ed2ceSHans Rosenfeld 	 */
3562a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & CCID_IO_F_IN_PROGRESS) == 0) {
3563a61ed2ceSHans Rosenfeld 		return;
3564a61ed2ceSHans Rosenfeld 	}
3565a61ed2ceSHans Rosenfeld 
3566a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_errno = error;
3567a61ed2ceSHans Rosenfeld 	ccid_user_io_done(ccid, slot);
3568a61ed2ceSHans Rosenfeld 
3569a61ed2ceSHans Rosenfeld 	/*
3570a61ed2ceSHans Rosenfeld 	 * There is still I/O going on. We need to mark this on the slot such
3571a61ed2ceSHans Rosenfeld 	 * that no one can gain ownership of it or issue commands. This will
3572a61ed2ceSHans Rosenfeld 	 * block hand off of a slot.
3573a61ed2ceSHans Rosenfeld 	 */
3574a61ed2ceSHans Rosenfeld 	slot->cs_flags |= CCID_SLOT_F_NEED_IO_TEARDOWN;
3575a61ed2ceSHans Rosenfeld }
3576a61ed2ceSHans Rosenfeld 
3577a61ed2ceSHans Rosenfeld /*
3578a61ed2ceSHans Rosenfeld  * This function is called in response to a CCID command completing.
3579a61ed2ceSHans Rosenfeld  */
3580a61ed2ceSHans Rosenfeld static void
ccid_complete_apdu(ccid_t * ccid,ccid_slot_t * slot,ccid_command_t * cc)3581a61ed2ceSHans Rosenfeld ccid_complete_apdu(ccid_t *ccid, ccid_slot_t *slot, ccid_command_t *cc)
3582a61ed2ceSHans Rosenfeld {
3583a61ed2ceSHans Rosenfeld 	ccid_reply_command_status_t crs;
3584a61ed2ceSHans Rosenfeld 	ccid_reply_icc_status_t cis;
3585a61ed2ceSHans Rosenfeld 	ccid_command_err_t cce;
3586a61ed2ceSHans Rosenfeld 
3587a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
3588a61ed2ceSHans Rosenfeld 	VERIFY3P(slot->cs_io.ci_command, ==, cc);
3589a61ed2ceSHans Rosenfeld 
3590a61ed2ceSHans Rosenfeld 	/*
3591a61ed2ceSHans Rosenfeld 	 * This completion could be called due to the fact that a user is no
3592a61ed2ceSHans Rosenfeld 	 * longer present, but we still have outstanding work to do in the
3593a61ed2ceSHans Rosenfeld 	 * stack. As such, we need to go through and check if the flag was set
3594a61ed2ceSHans Rosenfeld 	 * on the slot during teardown and if so, clean it up now.
3595a61ed2ceSHans Rosenfeld 	 */
3596a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_NEED_IO_TEARDOWN) != 0) {
3597a61ed2ceSHans Rosenfeld 		ccid_command_free(cc);
3598a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_command = NULL;
3599a61ed2ceSHans Rosenfeld 		ccid_slot_io_teardown_done(slot);
3600a61ed2ceSHans Rosenfeld 		return;
3601a61ed2ceSHans Rosenfeld 	}
3602a61ed2ceSHans Rosenfeld 
3603a61ed2ceSHans Rosenfeld 	/*
3604a61ed2ceSHans Rosenfeld 	 * Process this command and figure out what we should logically be
3605a61ed2ceSHans Rosenfeld 	 * returning to the user.
3606a61ed2ceSHans Rosenfeld 	 */
3607a61ed2ceSHans Rosenfeld 	if (cc->cc_state != CCID_COMMAND_COMPLETE) {
3608a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_errno = EIO;
3609a61ed2ceSHans Rosenfeld 		slot->cs_flags |= CCID_SLOT_F_NEED_TXN_RESET;
3610a61ed2ceSHans Rosenfeld 		ccid_worker_request(ccid);
3611a61ed2ceSHans Rosenfeld 		goto consume;
3612a61ed2ceSHans Rosenfeld 	}
3613a61ed2ceSHans Rosenfeld 
3614a61ed2ceSHans Rosenfeld 	ccid_command_status_decode(cc, &crs, &cis, &cce);
3615a61ed2ceSHans Rosenfeld 	if (crs == CCID_REPLY_STATUS_COMPLETE) {
3616a61ed2ceSHans Rosenfeld 		mblk_t *mp;
3617a61ed2ceSHans Rosenfeld 
3618a61ed2ceSHans Rosenfeld 		mp = cc->cc_response;
3619a61ed2ceSHans Rosenfeld 		cc->cc_response = NULL;
3620a61ed2ceSHans Rosenfeld 		mp->b_rptr += sizeof (ccid_header_t);
3621a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_errno = 0;
3622a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_data = mp;
3623a61ed2ceSHans Rosenfeld 	} else if (cis == CCID_REPLY_ICC_MISSING) {
3624a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_errno = ENXIO;
3625a61ed2ceSHans Rosenfeld 	} else {
3626a61ed2ceSHans Rosenfeld 		/*
3627a61ed2ceSHans Rosenfeld 		 * There are a few more semantic things we can do
3628a61ed2ceSHans Rosenfeld 		 * with the errors here that we're throwing out and
3629a61ed2ceSHans Rosenfeld 		 * lumping as EIO. Oh well.
3630a61ed2ceSHans Rosenfeld 		 */
3631a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_errno = EIO;
3632a61ed2ceSHans Rosenfeld 	}
3633a61ed2ceSHans Rosenfeld 
3634a61ed2ceSHans Rosenfeld 	/*
3635a61ed2ceSHans Rosenfeld 	 * Now, we can go ahead and wake up a reader to process this command.
3636a61ed2ceSHans Rosenfeld 	 */
3637a61ed2ceSHans Rosenfeld consume:
3638a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_command = NULL;
3639a61ed2ceSHans Rosenfeld 	ccid_command_free(cc);
3640a61ed2ceSHans Rosenfeld 	ccid_user_io_done(ccid, slot);
3641a61ed2ceSHans Rosenfeld }
3642a61ed2ceSHans Rosenfeld 
3643a61ed2ceSHans Rosenfeld /*
3644a61ed2ceSHans Rosenfeld  * We have the user buffer in the CCID slot. Given that, transform it into
3645a61ed2ceSHans Rosenfeld  * something that we can send to the device. For APDU's this is simply creating
3646a61ed2ceSHans Rosenfeld  * a transfer command and copying it into that buffer.
3647a61ed2ceSHans Rosenfeld  */
3648a61ed2ceSHans Rosenfeld static int
ccid_write_apdu(ccid_t * ccid,ccid_slot_t * slot)3649a61ed2ceSHans Rosenfeld ccid_write_apdu(ccid_t *ccid, ccid_slot_t *slot)
3650a61ed2ceSHans Rosenfeld {
3651a61ed2ceSHans Rosenfeld 	int ret;
3652a61ed2ceSHans Rosenfeld 	ccid_command_t *cc;
3653a61ed2ceSHans Rosenfeld 
3654a61ed2ceSHans Rosenfeld 	VERIFY(MUTEX_HELD(&ccid->ccid_mutex));
3655a61ed2ceSHans Rosenfeld 
3656a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_alloc(ccid, slot, B_FALSE, NULL,
3657a61ed2ceSHans Rosenfeld 	    slot->cs_io.ci_ilen, CCID_REQUEST_TRANSFER_BLOCK, 0, 0, 0,
3658a61ed2ceSHans Rosenfeld 	    &cc)) != 0) {
3659a61ed2ceSHans Rosenfeld 		return (ret);
3660a61ed2ceSHans Rosenfeld 	}
3661a61ed2ceSHans Rosenfeld 
3662a61ed2ceSHans Rosenfeld 	cc->cc_flags |= CCID_COMMAND_F_USER;
3663a61ed2ceSHans Rosenfeld 	ccid_command_bcopy(cc, slot->cs_io.ci_ibuf, slot->cs_io.ci_ilen);
3664a61ed2ceSHans Rosenfeld 
3665a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_command = cc;
3666a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3667a61ed2ceSHans Rosenfeld 
3668a61ed2ceSHans Rosenfeld 	if ((ret = ccid_command_queue(ccid, cc)) != 0) {
3669a61ed2ceSHans Rosenfeld 		mutex_enter(&ccid->ccid_mutex);
3670a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_command = NULL;
3671a61ed2ceSHans Rosenfeld 		ccid_command_free(cc);
3672a61ed2ceSHans Rosenfeld 		return (ret);
3673a61ed2ceSHans Rosenfeld 	}
3674a61ed2ceSHans Rosenfeld 
3675a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3676a61ed2ceSHans Rosenfeld 
3677a61ed2ceSHans Rosenfeld 	return (0);
3678a61ed2ceSHans Rosenfeld }
3679a61ed2ceSHans Rosenfeld 
3680a61ed2ceSHans Rosenfeld static int
ccid_read(dev_t dev,struct uio * uiop,cred_t * credp)3681a61ed2ceSHans Rosenfeld ccid_read(dev_t dev, struct uio *uiop, cred_t *credp)
3682a61ed2ceSHans Rosenfeld {
3683a61ed2ceSHans Rosenfeld 	int ret;
3684a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *idx;
3685a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
3686a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
3687a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
3688a61ed2ceSHans Rosenfeld 	boolean_t done;
3689a61ed2ceSHans Rosenfeld 
3690a61ed2ceSHans Rosenfeld 	if (uiop->uio_resid <= 0) {
3691a61ed2ceSHans Rosenfeld 		return (EINVAL);
3692a61ed2ceSHans Rosenfeld 	}
3693a61ed2ceSHans Rosenfeld 
3694a61ed2ceSHans Rosenfeld 	if ((idx = ccid_minor_find_user(getminor(dev))) == NULL) {
3695a61ed2ceSHans Rosenfeld 		return (ENOENT);
3696a61ed2ceSHans Rosenfeld 	}
3697a61ed2ceSHans Rosenfeld 
3698a61ed2ceSHans Rosenfeld 	cmp = idx->cmi_data.cmi_user;
3699a61ed2ceSHans Rosenfeld 	slot = cmp->cm_slot;
3700a61ed2ceSHans Rosenfeld 	ccid = slot->cs_ccid;
3701a61ed2ceSHans Rosenfeld 
3702a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3703a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
3704a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3705a61ed2ceSHans Rosenfeld 		return (ENODEV);
3706a61ed2ceSHans Rosenfeld 	}
3707a61ed2ceSHans Rosenfeld 
3708a61ed2ceSHans Rosenfeld 	/*
3709a61ed2ceSHans Rosenfeld 	 * First, check if we have exclusive access. If not, we're done.
3710a61ed2ceSHans Rosenfeld 	 */
3711a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) == 0) {
3712a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3713a61ed2ceSHans Rosenfeld 		return (EACCES);
3714a61ed2ceSHans Rosenfeld 	}
3715a61ed2ceSHans Rosenfeld 
3716a61ed2ceSHans Rosenfeld 	/*
3717a61ed2ceSHans Rosenfeld 	 * While it's tempting to mirror ccid_write() here and check if we have
3718a61ed2ceSHans Rosenfeld 	 * a tx or rx function, that actually has no relevance on read. The only
3719a61ed2ceSHans Rosenfeld 	 * thing that matters is whether or not we actually have an I/O.
3720a61ed2ceSHans Rosenfeld 	 */
3721a61ed2ceSHans Rosenfeld 
3722a61ed2ceSHans Rosenfeld 	/*
3723a61ed2ceSHans Rosenfeld 	 * If there's been no write I/O issued, then this read is not allowed.
3724a61ed2ceSHans Rosenfeld 	 * While this may seem like a silly constraint, it certainly simplifies
3725a61ed2ceSHans Rosenfeld 	 * a lot of the surrounding logic and fits with the current consumer
3726a61ed2ceSHans Rosenfeld 	 * model.
3727a61ed2ceSHans Rosenfeld 	 */
3728a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & (CCID_IO_F_IN_PROGRESS | CCID_IO_F_DONE)) ==
3729a61ed2ceSHans Rosenfeld 	    0) {
3730a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3731a61ed2ceSHans Rosenfeld 		return (ENODATA);
3732a61ed2ceSHans Rosenfeld 	}
3733a61ed2ceSHans Rosenfeld 
3734a61ed2ceSHans Rosenfeld 	/*
3735a61ed2ceSHans Rosenfeld 	 * If another thread is already blocked in read, then don't allow us
3736a61ed2ceSHans Rosenfeld 	 * in. We only want to allow one thread to attempt to consume a read,
3737a61ed2ceSHans Rosenfeld 	 * just as we only allow one thread to initiate a write.
3738a61ed2ceSHans Rosenfeld 	 */
3739a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_READ_WAITING) != 0) {
3740a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3741a61ed2ceSHans Rosenfeld 		return (EBUSY);
3742a61ed2ceSHans Rosenfeld 	}
3743a61ed2ceSHans Rosenfeld 
3744a61ed2ceSHans Rosenfeld 	/*
3745a61ed2ceSHans Rosenfeld 	 * Check if an I/O has completed. Once it has, call the protocol
3746a61ed2ceSHans Rosenfeld 	 * specific code. Note that the lock may be dropped after polling. In
3747a61ed2ceSHans Rosenfeld 	 * such a case we will have to logically recheck several conditions.
3748a61ed2ceSHans Rosenfeld 	 *
3749a61ed2ceSHans Rosenfeld 	 * Note, we don't really care if the slot is active or not as I/O could
3750a61ed2ceSHans Rosenfeld 	 * have been in flight while the slot was inactive.
3751a61ed2ceSHans Rosenfeld 	 */
3752a61ed2ceSHans Rosenfeld 	while ((slot->cs_io.ci_flags & CCID_IO_F_DONE) == 0) {
3753a61ed2ceSHans Rosenfeld 		if (uiop->uio_fmode & FNONBLOCK) {
3754a61ed2ceSHans Rosenfeld 			mutex_exit(&ccid->ccid_mutex);
3755a61ed2ceSHans Rosenfeld 			return (EWOULDBLOCK);
3756a61ed2ceSHans Rosenfeld 		}
3757a61ed2ceSHans Rosenfeld 
3758a61ed2ceSHans Rosenfeld 		/*
3759a61ed2ceSHans Rosenfeld 		 * While we perform a cv_wait_sig() we'll end up dropping the
3760a61ed2ceSHans Rosenfeld 		 * CCID mutex. This means that we need to notify the rest of the
3761a61ed2ceSHans Rosenfeld 		 * driver that a thread is blocked in read. This is used not
3762a61ed2ceSHans Rosenfeld 		 * only for excluding multiple threads trying to read from the
3763a61ed2ceSHans Rosenfeld 		 * device, but more importantly so that we know that if the ICC
3764a61ed2ceSHans Rosenfeld 		 * or reader are removed, that we need to wake up this thread.
3765a61ed2ceSHans Rosenfeld 		 */
3766a61ed2ceSHans Rosenfeld 		cmp->cm_flags |= CCID_MINOR_F_READ_WAITING;
3767a61ed2ceSHans Rosenfeld 		ret = cv_wait_sig(&cmp->cm_read_cv, &ccid->ccid_mutex);
3768a61ed2ceSHans Rosenfeld 		cmp->cm_flags &= ~CCID_MINOR_F_READ_WAITING;
3769a61ed2ceSHans Rosenfeld 		cv_signal(&cmp->cm_iowait_cv);
3770a61ed2ceSHans Rosenfeld 
3771a61ed2ceSHans Rosenfeld 		if (ret == 0) {
3772a61ed2ceSHans Rosenfeld 			mutex_exit(&ccid->ccid_mutex);
3773a61ed2ceSHans Rosenfeld 			return (EINTR);
3774a61ed2ceSHans Rosenfeld 		}
3775a61ed2ceSHans Rosenfeld 
3776a61ed2ceSHans Rosenfeld 		/*
3777a61ed2ceSHans Rosenfeld 		 * Check if the reader has been removed. We do not need to check
3778a61ed2ceSHans Rosenfeld 		 * for other conditions, as we'll end up being told that the I/O
3779a61ed2ceSHans Rosenfeld 		 * is done and that the error has been set.
3780a61ed2ceSHans Rosenfeld 		 */
3781a61ed2ceSHans Rosenfeld 		if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
3782a61ed2ceSHans Rosenfeld 			mutex_exit(&ccid->ccid_mutex);
3783a61ed2ceSHans Rosenfeld 			return (ENODEV);
3784a61ed2ceSHans Rosenfeld 		}
3785a61ed2ceSHans Rosenfeld 	}
3786a61ed2ceSHans Rosenfeld 
3787a61ed2ceSHans Rosenfeld 	/*
3788a61ed2ceSHans Rosenfeld 	 * We'll either have an error or data available for the user at this
3789a61ed2ceSHans Rosenfeld 	 * point that we can copy out. We need to make sure that it's not too
3790a61ed2ceSHans Rosenfeld 	 * large. The data should have already been adjusted such that we only
3791a61ed2ceSHans Rosenfeld 	 * have data payloads.
3792a61ed2ceSHans Rosenfeld 	 */
3793a61ed2ceSHans Rosenfeld 	done = B_FALSE;
3794a61ed2ceSHans Rosenfeld 	if (slot->cs_io.ci_errno == 0) {
3795a61ed2ceSHans Rosenfeld 		size_t mlen;
3796a61ed2ceSHans Rosenfeld 
3797a61ed2ceSHans Rosenfeld 		mlen = msgsize(slot->cs_io.ci_data);
3798a61ed2ceSHans Rosenfeld 		if (mlen > uiop->uio_resid) {
3799a61ed2ceSHans Rosenfeld 			ret = EOVERFLOW;
3800a61ed2ceSHans Rosenfeld 		} else {
3801a61ed2ceSHans Rosenfeld 			ret = ccid_read_copyout(uiop, slot->cs_io.ci_data);
3802a61ed2ceSHans Rosenfeld 			if (ret == 0) {
3803a61ed2ceSHans Rosenfeld 				done = B_TRUE;
3804a61ed2ceSHans Rosenfeld 			}
3805a61ed2ceSHans Rosenfeld 		}
3806a61ed2ceSHans Rosenfeld 	} else {
3807a61ed2ceSHans Rosenfeld 		ret = slot->cs_io.ci_errno;
3808a61ed2ceSHans Rosenfeld 		done = B_TRUE;
3809a61ed2ceSHans Rosenfeld 	}
3810a61ed2ceSHans Rosenfeld 
3811a61ed2ceSHans Rosenfeld 	if (done) {
3812a61ed2ceSHans Rosenfeld 		ccid_clear_io(&slot->cs_io);
3813a61ed2ceSHans Rosenfeld 		ccid_slot_pollout_signal(slot);
3814a61ed2ceSHans Rosenfeld 	}
3815a61ed2ceSHans Rosenfeld 
3816a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3817a61ed2ceSHans Rosenfeld 
3818a61ed2ceSHans Rosenfeld 	return (ret);
3819a61ed2ceSHans Rosenfeld }
3820a61ed2ceSHans Rosenfeld 
3821a61ed2ceSHans Rosenfeld static int
ccid_write(dev_t dev,struct uio * uiop,cred_t * credp)3822a61ed2ceSHans Rosenfeld ccid_write(dev_t dev, struct uio *uiop, cred_t *credp)
3823a61ed2ceSHans Rosenfeld {
3824a61ed2ceSHans Rosenfeld 	int ret;
3825a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *idx;
3826a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
3827a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
3828a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
3829a61ed2ceSHans Rosenfeld 	size_t len, cbytes;
3830a61ed2ceSHans Rosenfeld 
3831a61ed2ceSHans Rosenfeld 	if (uiop->uio_resid > CCID_APDU_LEN_MAX) {
3832a61ed2ceSHans Rosenfeld 		return (E2BIG);
3833a61ed2ceSHans Rosenfeld 	}
3834a61ed2ceSHans Rosenfeld 
3835a61ed2ceSHans Rosenfeld 	if (uiop->uio_resid <= 0) {
3836a61ed2ceSHans Rosenfeld 		return (EINVAL);
3837a61ed2ceSHans Rosenfeld 	}
3838a61ed2ceSHans Rosenfeld 
3839a61ed2ceSHans Rosenfeld 	len = uiop->uio_resid;
3840a61ed2ceSHans Rosenfeld 	idx = ccid_minor_find_user(getminor(dev));
3841a61ed2ceSHans Rosenfeld 	if (idx == NULL) {
3842a61ed2ceSHans Rosenfeld 		return (ENOENT);
3843a61ed2ceSHans Rosenfeld 	}
3844a61ed2ceSHans Rosenfeld 
3845a61ed2ceSHans Rosenfeld 	cmp = idx->cmi_data.cmi_user;
3846a61ed2ceSHans Rosenfeld 	slot = cmp->cm_slot;
3847a61ed2ceSHans Rosenfeld 	ccid = slot->cs_ccid;
3848a61ed2ceSHans Rosenfeld 
3849a61ed2ceSHans Rosenfeld 	/*
3850a61ed2ceSHans Rosenfeld 	 * Now that we have the slot, verify whether or not we can perform this
3851a61ed2ceSHans Rosenfeld 	 * I/O.
3852a61ed2ceSHans Rosenfeld 	 */
3853a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
3854a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
3855a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3856a61ed2ceSHans Rosenfeld 		return (ENODEV);
3857a61ed2ceSHans Rosenfeld 	}
3858a61ed2ceSHans Rosenfeld 
3859a61ed2ceSHans Rosenfeld 	/*
3860a61ed2ceSHans Rosenfeld 	 * Check that we are open for writing, have exclusive access, and
3861a61ed2ceSHans Rosenfeld 	 * there's a card present. If not, error out.
3862a61ed2ceSHans Rosenfeld 	 */
3863a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) !=
3864a61ed2ceSHans Rosenfeld 	    (CCID_MINOR_F_WRITABLE | CCID_MINOR_F_HAS_EXCL)) {
3865a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3866a61ed2ceSHans Rosenfeld 		return (EACCES);
3867a61ed2ceSHans Rosenfeld 	}
3868a61ed2ceSHans Rosenfeld 
3869a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) == 0) {
3870a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3871a61ed2ceSHans Rosenfeld 		return (ENXIO);
3872a61ed2ceSHans Rosenfeld 	}
3873a61ed2ceSHans Rosenfeld 
3874a61ed2ceSHans Rosenfeld 	/*
3875a61ed2ceSHans Rosenfeld 	 * Make sure that we have a supported transmit function.
3876a61ed2ceSHans Rosenfeld 	 */
3877a61ed2ceSHans Rosenfeld 	if (slot->cs_icc.icc_tx == NULL) {
3878a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3879a61ed2ceSHans Rosenfeld 		return (ENOTSUP);
3880a61ed2ceSHans Rosenfeld 	}
3881a61ed2ceSHans Rosenfeld 
3882a61ed2ceSHans Rosenfeld 	/*
3883a61ed2ceSHans Rosenfeld 	 * See if another command is in progress. If so, try to claim it.
3884a61ed2ceSHans Rosenfeld 	 * Otherwise, fail with EBUSY. Note, we only fail for commands that are
3885a61ed2ceSHans Rosenfeld 	 * user initiated. There may be other commands that are ongoing in the
3886a61ed2ceSHans Rosenfeld 	 * system.
3887a61ed2ceSHans Rosenfeld 	 */
3888a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & CCID_IO_F_POLLOUT_FLAGS) != 0) {
3889a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3890a61ed2ceSHans Rosenfeld 		return (EBUSY);
3891a61ed2ceSHans Rosenfeld 	}
3892a61ed2ceSHans Rosenfeld 
3893a61ed2ceSHans Rosenfeld 	/*
3894a61ed2ceSHans Rosenfeld 	 * Use uiocopy and not uiomove. This way if we fail for whatever reason,
3895a61ed2ceSHans Rosenfeld 	 * we don't have to worry about restoring the original buffer.
3896a61ed2ceSHans Rosenfeld 	 */
3897a61ed2ceSHans Rosenfeld 	if (uiocopy(slot->cs_io.ci_ibuf, len, UIO_WRITE, uiop, &cbytes) != 0) {
3898a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
3899a61ed2ceSHans Rosenfeld 		return (EFAULT);
3900a61ed2ceSHans Rosenfeld 	}
3901a61ed2ceSHans Rosenfeld 
3902a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_ilen = len;
3903a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_flags |= CCID_IO_F_PREPARING;
3904a61ed2ceSHans Rosenfeld 	slot->cs_io.ci_omp = NULL;
3905a61ed2ceSHans Rosenfeld 
3906a61ed2ceSHans Rosenfeld 	/*
3907a61ed2ceSHans Rosenfeld 	 * Now that we're here, go ahead and call the actual tx function.
3908a61ed2ceSHans Rosenfeld 	 */
3909a61ed2ceSHans Rosenfeld 	if ((ret = slot->cs_icc.icc_tx(ccid, slot)) != 0) {
3910a61ed2ceSHans Rosenfeld 		/*
3911a61ed2ceSHans Rosenfeld 		 * The command wasn't actually transmitted. In this case we need
3912a61ed2ceSHans Rosenfeld 		 * to reset the copied in data and signal anyone who is polling
3913a61ed2ceSHans Rosenfeld 		 * that this is writeable again. We don't have to worry about
3914a61ed2ceSHans Rosenfeld 		 * readers at this point, as they won't get in unless
3915a61ed2ceSHans Rosenfeld 		 * CCID_IO_F_IN_PROGRESS has been set.
3916a61ed2ceSHans Rosenfeld 		 */
3917a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_ilen = 0;
3918a61ed2ceSHans Rosenfeld 		bzero(slot->cs_io.ci_ibuf, sizeof (slot->cs_io.ci_ibuf));
3919a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_flags &= ~CCID_IO_F_PREPARING;
3920a61ed2ceSHans Rosenfeld 
3921a61ed2ceSHans Rosenfeld 		ccid_slot_pollout_signal(slot);
3922a61ed2ceSHans Rosenfeld 	} else {
3923a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_flags &= ~CCID_IO_F_PREPARING;
3924a61ed2ceSHans Rosenfeld 		slot->cs_io.ci_flags |= CCID_IO_F_IN_PROGRESS;
3925a61ed2ceSHans Rosenfeld 		uiop->uio_resid -= cbytes;
3926a61ed2ceSHans Rosenfeld 	}
3927a61ed2ceSHans Rosenfeld 	/*
3928a61ed2ceSHans Rosenfeld 	 * Notify a waiter that we've moved on.
3929a61ed2ceSHans Rosenfeld 	 */
3930a61ed2ceSHans Rosenfeld 	cv_signal(&slot->cs_excl_minor->cm_iowait_cv);
3931a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
3932a61ed2ceSHans Rosenfeld 
3933a61ed2ceSHans Rosenfeld 	return (ret);
3934a61ed2ceSHans Rosenfeld }
3935a61ed2ceSHans Rosenfeld 
3936a61ed2ceSHans Rosenfeld static int
ccid_ioctl_status(ccid_slot_t * slot,intptr_t arg,int mode)3937a61ed2ceSHans Rosenfeld ccid_ioctl_status(ccid_slot_t *slot, intptr_t arg, int mode)
3938a61ed2ceSHans Rosenfeld {
3939a61ed2ceSHans Rosenfeld 	uccid_cmd_status_t ucs;
3940a61ed2ceSHans Rosenfeld 	ccid_t *ccid = slot->cs_ccid;
3941a61ed2ceSHans Rosenfeld 
3942a61ed2ceSHans Rosenfeld 	if (ddi_copyin((void *)arg, &ucs, sizeof (ucs), mode & FKIOCTL) != 0)
3943a61ed2ceSHans Rosenfeld 		return (EFAULT);
3944a61ed2ceSHans Rosenfeld 
3945a61ed2ceSHans Rosenfeld 	if (ucs.ucs_version != UCCID_VERSION_ONE)
3946a61ed2ceSHans Rosenfeld 		return (EINVAL);
3947a61ed2ceSHans Rosenfeld 
3948a61ed2ceSHans Rosenfeld 	ucs.ucs_status = 0;
3949a61ed2ceSHans Rosenfeld 	ucs.ucs_instance = ddi_get_instance(slot->cs_ccid->ccid_dip);
3950a61ed2ceSHans Rosenfeld 	ucs.ucs_slot = slot->cs_slotno;
3951a61ed2ceSHans Rosenfeld 
3952a61ed2ceSHans Rosenfeld 	mutex_enter(&slot->cs_ccid->ccid_mutex);
3953a61ed2ceSHans Rosenfeld 	if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
3954a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
3955a61ed2ceSHans Rosenfeld 		return (ENODEV);
3956a61ed2ceSHans Rosenfeld 	}
3957a61ed2ceSHans Rosenfeld 
3958a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_PRESENT) != 0)
3959a61ed2ceSHans Rosenfeld 		ucs.ucs_status |= UCCID_STATUS_F_CARD_PRESENT;
3960a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0)
3961a61ed2ceSHans Rosenfeld 		ucs.ucs_status |= UCCID_STATUS_F_CARD_ACTIVE;
3962a61ed2ceSHans Rosenfeld 
3963a61ed2ceSHans Rosenfeld 	if (slot->cs_atr != NULL) {
3964a61ed2ceSHans Rosenfeld 		ucs.ucs_atrlen = MIN(UCCID_ATR_MAX, MBLKL(slot->cs_atr));
3965a61ed2ceSHans Rosenfeld 		bcopy(slot->cs_atr->b_rptr, ucs.ucs_atr, ucs.ucs_atrlen);
3966a61ed2ceSHans Rosenfeld 	} else {
3967a61ed2ceSHans Rosenfeld 		bzero(ucs.ucs_atr, sizeof (ucs.ucs_atr));
3968a61ed2ceSHans Rosenfeld 		ucs.ucs_atrlen = 0;
3969a61ed2ceSHans Rosenfeld 	}
3970a61ed2ceSHans Rosenfeld 
3971a61ed2ceSHans Rosenfeld 	bcopy(&ccid->ccid_class, &ucs.ucs_class, sizeof (ucs.ucs_class));
3972a61ed2ceSHans Rosenfeld 
3973a61ed2ceSHans Rosenfeld 	if (ccid->ccid_dev_data->dev_product != NULL) {
3974a61ed2ceSHans Rosenfeld 		(void) strlcpy(ucs.ucs_product,
3975a61ed2ceSHans Rosenfeld 		    ccid->ccid_dev_data->dev_product, sizeof (ucs.ucs_product));
3976a61ed2ceSHans Rosenfeld 		ucs.ucs_status |= UCCID_STATUS_F_PRODUCT_VALID;
3977a61ed2ceSHans Rosenfeld 	} else {
3978a61ed2ceSHans Rosenfeld 		ucs.ucs_product[0] = '\0';
3979a61ed2ceSHans Rosenfeld 	}
3980a61ed2ceSHans Rosenfeld 
3981a61ed2ceSHans Rosenfeld 	if (ccid->ccid_dev_data->dev_serial != NULL) {
3982a61ed2ceSHans Rosenfeld 		(void) strlcpy(ucs.ucs_serial, ccid->ccid_dev_data->dev_serial,
3983a61ed2ceSHans Rosenfeld 		    sizeof (ucs.ucs_serial));
3984a61ed2ceSHans Rosenfeld 		ucs.ucs_status |= UCCID_STATUS_F_SERIAL_VALID;
3985a61ed2ceSHans Rosenfeld 	} else {
3986a61ed2ceSHans Rosenfeld 		ucs.ucs_serial[0] = '\0';
3987a61ed2ceSHans Rosenfeld 	}
3988a61ed2ceSHans Rosenfeld 	mutex_exit(&slot->cs_ccid->ccid_mutex);
3989a61ed2ceSHans Rosenfeld 
3990a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0) {
3991a61ed2ceSHans Rosenfeld 		ucs.ucs_status |= UCCID_STATUS_F_PARAMS_VALID;
3992ae5c3fb7SToomas Soome 		ucs.ucs_prot = (uccid_prot_t)slot->cs_icc.icc_cur_protocol;
3993a61ed2ceSHans Rosenfeld 		ucs.ucs_params = slot->cs_icc.icc_params;
3994a61ed2ceSHans Rosenfeld 	}
3995a61ed2ceSHans Rosenfeld 
3996a61ed2ceSHans Rosenfeld 	if (ddi_copyout(&ucs, (void *)arg, sizeof (ucs), mode & FKIOCTL) != 0)
3997a61ed2ceSHans Rosenfeld 		return (EFAULT);
3998a61ed2ceSHans Rosenfeld 
3999a61ed2ceSHans Rosenfeld 	return (0);
4000a61ed2ceSHans Rosenfeld }
4001a61ed2ceSHans Rosenfeld 
4002a61ed2ceSHans Rosenfeld static int
ccid_ioctl_txn_begin(ccid_slot_t * slot,ccid_minor_t * cmp,intptr_t arg,int mode)4003a61ed2ceSHans Rosenfeld ccid_ioctl_txn_begin(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg,
4004a61ed2ceSHans Rosenfeld     int mode)
4005a61ed2ceSHans Rosenfeld {
4006a61ed2ceSHans Rosenfeld 	int ret;
4007a61ed2ceSHans Rosenfeld 	uccid_cmd_txn_begin_t uct;
4008a61ed2ceSHans Rosenfeld 	boolean_t nowait;
4009a61ed2ceSHans Rosenfeld 
4010a61ed2ceSHans Rosenfeld 	if (ddi_copyin((void *)arg, &uct, sizeof (uct), mode & FKIOCTL) != 0)
4011a61ed2ceSHans Rosenfeld 		return (EFAULT);
4012a61ed2ceSHans Rosenfeld 
4013a61ed2ceSHans Rosenfeld 	if (uct.uct_version != UCCID_VERSION_ONE)
4014a61ed2ceSHans Rosenfeld 		return (EINVAL);
4015a61ed2ceSHans Rosenfeld 
4016a61ed2ceSHans Rosenfeld 	if ((uct.uct_flags & ~UCCID_TXN_DONT_BLOCK) != 0)
4017a61ed2ceSHans Rosenfeld 		return (EINVAL);
4018a61ed2ceSHans Rosenfeld 	nowait = (uct.uct_flags & UCCID_TXN_DONT_BLOCK) != 0;
4019a61ed2ceSHans Rosenfeld 
4020a61ed2ceSHans Rosenfeld 	mutex_enter(&slot->cs_ccid->ccid_mutex);
4021a61ed2ceSHans Rosenfeld 	if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
4022a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4023a61ed2ceSHans Rosenfeld 		return (ENODEV);
4024a61ed2ceSHans Rosenfeld 	}
4025a61ed2ceSHans Rosenfeld 
4026a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_WRITABLE) == 0) {
4027a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4028a61ed2ceSHans Rosenfeld 		return (EBADF);
4029a61ed2ceSHans Rosenfeld 	}
4030a61ed2ceSHans Rosenfeld 
4031a61ed2ceSHans Rosenfeld 	ret = ccid_slot_excl_req(slot, cmp, nowait);
4032a61ed2ceSHans Rosenfeld 	mutex_exit(&slot->cs_ccid->ccid_mutex);
4033a61ed2ceSHans Rosenfeld 
4034a61ed2ceSHans Rosenfeld 	return (ret);
4035a61ed2ceSHans Rosenfeld }
4036a61ed2ceSHans Rosenfeld 
4037a61ed2ceSHans Rosenfeld static int
ccid_ioctl_txn_end(ccid_slot_t * slot,ccid_minor_t * cmp,intptr_t arg,int mode)4038a61ed2ceSHans Rosenfeld ccid_ioctl_txn_end(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg, int mode)
4039a61ed2ceSHans Rosenfeld {
4040a61ed2ceSHans Rosenfeld 	uccid_cmd_txn_end_t uct;
4041a61ed2ceSHans Rosenfeld 
4042a61ed2ceSHans Rosenfeld 	if (ddi_copyin((void *)arg, &uct, sizeof (uct), mode & FKIOCTL) != 0) {
4043a61ed2ceSHans Rosenfeld 		return (EFAULT);
4044a61ed2ceSHans Rosenfeld 	}
4045a61ed2ceSHans Rosenfeld 
4046a61ed2ceSHans Rosenfeld 	if (uct.uct_version != UCCID_VERSION_ONE) {
4047a61ed2ceSHans Rosenfeld 		return (EINVAL);
4048a61ed2ceSHans Rosenfeld 	}
4049a61ed2ceSHans Rosenfeld 
4050a61ed2ceSHans Rosenfeld 	mutex_enter(&slot->cs_ccid->ccid_mutex);
4051a61ed2ceSHans Rosenfeld 	if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
4052a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4053a61ed2ceSHans Rosenfeld 		return (ENODEV);
4054a61ed2ceSHans Rosenfeld 	}
4055a61ed2ceSHans Rosenfeld 
4056a61ed2ceSHans Rosenfeld 	if (slot->cs_excl_minor != cmp) {
4057a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4058a61ed2ceSHans Rosenfeld 		return (EINVAL);
4059a61ed2ceSHans Rosenfeld 	}
4060a61ed2ceSHans Rosenfeld 	VERIFY3S(cmp->cm_flags & CCID_MINOR_F_HAS_EXCL, !=, 0);
4061a61ed2ceSHans Rosenfeld 
4062a61ed2ceSHans Rosenfeld 	/*
4063a61ed2ceSHans Rosenfeld 	 * Require exactly one of the flags to be set.
4064a61ed2ceSHans Rosenfeld 	 */
4065a61ed2ceSHans Rosenfeld 	switch (uct.uct_flags) {
4066a61ed2ceSHans Rosenfeld 	case UCCID_TXN_END_RESET:
4067a61ed2ceSHans Rosenfeld 		cmp->cm_flags |= CCID_MINOR_F_TXN_RESET;
4068a61ed2ceSHans Rosenfeld 
4069a61ed2ceSHans Rosenfeld 	case UCCID_TXN_END_RELEASE:
4070a61ed2ceSHans Rosenfeld 		break;
4071a61ed2ceSHans Rosenfeld 
4072a61ed2ceSHans Rosenfeld 	default:
4073a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4074a61ed2ceSHans Rosenfeld 		return (EINVAL);
4075a61ed2ceSHans Rosenfeld 	}
4076a61ed2ceSHans Rosenfeld 
4077a61ed2ceSHans Rosenfeld 	ccid_slot_excl_rele(slot);
4078a61ed2ceSHans Rosenfeld 	mutex_exit(&slot->cs_ccid->ccid_mutex);
4079a61ed2ceSHans Rosenfeld 
4080a61ed2ceSHans Rosenfeld 	return (0);
4081a61ed2ceSHans Rosenfeld }
4082a61ed2ceSHans Rosenfeld 
4083a61ed2ceSHans Rosenfeld static int
ccid_ioctl_fionread(ccid_slot_t * slot,ccid_minor_t * cmp,intptr_t arg,int mode)4084a61ed2ceSHans Rosenfeld ccid_ioctl_fionread(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg,
4085a61ed2ceSHans Rosenfeld     int mode)
4086a61ed2ceSHans Rosenfeld {
4087a61ed2ceSHans Rosenfeld 	int data;
4088a61ed2ceSHans Rosenfeld 
4089a61ed2ceSHans Rosenfeld 	mutex_enter(&slot->cs_ccid->ccid_mutex);
4090a61ed2ceSHans Rosenfeld 	if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
4091a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4092a61ed2ceSHans Rosenfeld 		return (ENODEV);
4093a61ed2ceSHans Rosenfeld 	}
4094a61ed2ceSHans Rosenfeld 
4095a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) == 0) {
4096a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4097a61ed2ceSHans Rosenfeld 		return (EACCES);
4098a61ed2ceSHans Rosenfeld 	}
4099a61ed2ceSHans Rosenfeld 
4100a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_WRITABLE) == 0) {
4101a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4102a61ed2ceSHans Rosenfeld 		return (EBADF);
4103a61ed2ceSHans Rosenfeld 	}
4104a61ed2ceSHans Rosenfeld 
4105a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) != 0) {
4106a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4107a61ed2ceSHans Rosenfeld 		return (ENODATA);
4108a61ed2ceSHans Rosenfeld 	}
4109a61ed2ceSHans Rosenfeld 
4110a61ed2ceSHans Rosenfeld 	/*
4111a61ed2ceSHans Rosenfeld 	 * If there's an error, claim that there's at least one byte to read
4112a61ed2ceSHans Rosenfeld 	 * even if it means we'll get the error and consume it. FIONREAD only
4113a61ed2ceSHans Rosenfeld 	 * allows up to an int of data. Realistically because we don't allow
4114a61ed2ceSHans Rosenfeld 	 * extended APDUs, the amount of data here should be always less than
4115a61ed2ceSHans Rosenfeld 	 * INT_MAX.
4116a61ed2ceSHans Rosenfeld 	 */
4117a61ed2ceSHans Rosenfeld 	if (slot->cs_io.ci_errno != 0) {
4118a61ed2ceSHans Rosenfeld 		data = 1;
4119a61ed2ceSHans Rosenfeld 	} else {
4120a61ed2ceSHans Rosenfeld 		size_t s = msgsize(slot->cs_io.ci_data);
4121a61ed2ceSHans Rosenfeld 		data = MIN(s, INT_MAX);
4122a61ed2ceSHans Rosenfeld 	}
4123a61ed2ceSHans Rosenfeld 
4124a61ed2ceSHans Rosenfeld 	if (ddi_copyout(&data, (void *)arg, sizeof (data), mode & FKIOCTL) !=
4125a61ed2ceSHans Rosenfeld 	    0) {
4126a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4127a61ed2ceSHans Rosenfeld 		return (EFAULT);
4128a61ed2ceSHans Rosenfeld 	}
4129a61ed2ceSHans Rosenfeld 
4130a61ed2ceSHans Rosenfeld 	mutex_exit(&slot->cs_ccid->ccid_mutex);
4131a61ed2ceSHans Rosenfeld 	return (0);
4132a61ed2ceSHans Rosenfeld }
4133a61ed2ceSHans Rosenfeld 
4134a61ed2ceSHans Rosenfeld static int
ccid_ioctl_icc_modify(ccid_slot_t * slot,ccid_minor_t * cmp,intptr_t arg,int mode)4135a61ed2ceSHans Rosenfeld ccid_ioctl_icc_modify(ccid_slot_t *slot, ccid_minor_t *cmp, intptr_t arg,
4136a61ed2ceSHans Rosenfeld     int mode)
4137a61ed2ceSHans Rosenfeld {
4138a61ed2ceSHans Rosenfeld 	int ret = 0;
4139a61ed2ceSHans Rosenfeld 	uccid_cmd_icc_modify_t uci;
4140a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
4141a61ed2ceSHans Rosenfeld 
4142a61ed2ceSHans Rosenfeld 	if (ddi_copyin((void *)arg, &uci, sizeof (uci), mode & FKIOCTL) != 0) {
4143a61ed2ceSHans Rosenfeld 		return (EFAULT);
4144a61ed2ceSHans Rosenfeld 	}
4145a61ed2ceSHans Rosenfeld 
4146a61ed2ceSHans Rosenfeld 	if (uci.uci_version != UCCID_VERSION_ONE) {
4147a61ed2ceSHans Rosenfeld 		return (EINVAL);
4148a61ed2ceSHans Rosenfeld 	}
4149a61ed2ceSHans Rosenfeld 
4150a61ed2ceSHans Rosenfeld 	switch (uci.uci_action) {
4151a61ed2ceSHans Rosenfeld 	case UCCID_ICC_POWER_ON:
4152a61ed2ceSHans Rosenfeld 	case UCCID_ICC_POWER_OFF:
4153a61ed2ceSHans Rosenfeld 	case UCCID_ICC_WARM_RESET:
4154a61ed2ceSHans Rosenfeld 		break;
4155a61ed2ceSHans Rosenfeld 	default:
4156a61ed2ceSHans Rosenfeld 		return (EINVAL);
4157a61ed2ceSHans Rosenfeld 	}
4158a61ed2ceSHans Rosenfeld 
4159a61ed2ceSHans Rosenfeld 	ccid = slot->cs_ccid;
4160a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
4161a61ed2ceSHans Rosenfeld 	if ((slot->cs_ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
4162a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4163a61ed2ceSHans Rosenfeld 		return (ENODEV);
4164a61ed2ceSHans Rosenfeld 	}
4165a61ed2ceSHans Rosenfeld 
4166a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_WRITABLE) == 0) {
4167a61ed2ceSHans Rosenfeld 		mutex_exit(&slot->cs_ccid->ccid_mutex);
4168a61ed2ceSHans Rosenfeld 		return (EBADF);
4169a61ed2ceSHans Rosenfeld 	}
4170a61ed2ceSHans Rosenfeld 
4171a61ed2ceSHans Rosenfeld 	switch (uci.uci_action) {
4172a61ed2ceSHans Rosenfeld 	case UCCID_ICC_WARM_RESET:
4173a61ed2ceSHans Rosenfeld 		ret = ccid_slot_warm_reset(ccid, slot);
4174a61ed2ceSHans Rosenfeld 		break;
4175a61ed2ceSHans Rosenfeld 
4176a61ed2ceSHans Rosenfeld 	case UCCID_ICC_POWER_OFF:
4177a61ed2ceSHans Rosenfeld 		ret = ccid_slot_power_off(ccid, slot);
4178a61ed2ceSHans Rosenfeld 		break;
4179a61ed2ceSHans Rosenfeld 
4180a61ed2ceSHans Rosenfeld 	case UCCID_ICC_POWER_ON:
4181a61ed2ceSHans Rosenfeld 		ret = ccid_slot_inserted(ccid, slot);
4182a61ed2ceSHans Rosenfeld 		break;
4183a61ed2ceSHans Rosenfeld 	}
4184a61ed2ceSHans Rosenfeld 
4185a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
4186a61ed2ceSHans Rosenfeld 
4187a61ed2ceSHans Rosenfeld 	return (ret);
4188a61ed2ceSHans Rosenfeld }
4189a61ed2ceSHans Rosenfeld 
4190a61ed2ceSHans Rosenfeld static int
ccid_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)4191a61ed2ceSHans Rosenfeld ccid_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
4192a61ed2ceSHans Rosenfeld     int *rvalp)
4193a61ed2ceSHans Rosenfeld {
4194a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *idx;
4195a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
4196a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
4197a61ed2ceSHans Rosenfeld 
4198a61ed2ceSHans Rosenfeld 	idx = ccid_minor_find_user(getminor(dev));
4199a61ed2ceSHans Rosenfeld 	if (idx == NULL) {
4200a61ed2ceSHans Rosenfeld 		return (ENOENT);
4201a61ed2ceSHans Rosenfeld 	}
4202a61ed2ceSHans Rosenfeld 
4203a61ed2ceSHans Rosenfeld 	cmp = idx->cmi_data.cmi_user;
4204a61ed2ceSHans Rosenfeld 	slot = cmp->cm_slot;
4205a61ed2ceSHans Rosenfeld 
4206a61ed2ceSHans Rosenfeld 	switch (cmd) {
4207a61ed2ceSHans Rosenfeld 	case UCCID_CMD_TXN_BEGIN:
4208a61ed2ceSHans Rosenfeld 		return (ccid_ioctl_txn_begin(slot, cmp, arg, mode));
4209a61ed2ceSHans Rosenfeld 	case UCCID_CMD_TXN_END:
4210a61ed2ceSHans Rosenfeld 		return (ccid_ioctl_txn_end(slot, cmp, arg, mode));
4211a61ed2ceSHans Rosenfeld 	case UCCID_CMD_STATUS:
4212a61ed2ceSHans Rosenfeld 		return (ccid_ioctl_status(slot, arg, mode));
4213a61ed2ceSHans Rosenfeld 	case FIONREAD:
4214a61ed2ceSHans Rosenfeld 		return (ccid_ioctl_fionread(slot, cmp, arg, mode));
4215a61ed2ceSHans Rosenfeld 	case UCCID_CMD_ICC_MODIFY:
4216a61ed2ceSHans Rosenfeld 		return (ccid_ioctl_icc_modify(slot, cmp, arg, mode));
4217a61ed2ceSHans Rosenfeld 	default:
4218a61ed2ceSHans Rosenfeld 		break;
4219a61ed2ceSHans Rosenfeld 	}
4220a61ed2ceSHans Rosenfeld 
4221a61ed2ceSHans Rosenfeld 	return (ENOTTY);
4222a61ed2ceSHans Rosenfeld }
4223a61ed2ceSHans Rosenfeld 
4224a61ed2ceSHans Rosenfeld static int
ccid_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)4225a61ed2ceSHans Rosenfeld ccid_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
4226a61ed2ceSHans Rosenfeld     struct pollhead **phpp)
4227a61ed2ceSHans Rosenfeld {
4228a61ed2ceSHans Rosenfeld 	short ready = 0;
4229a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *idx;
4230a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
4231a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
4232a61ed2ceSHans Rosenfeld 	ccid_t *ccid;
4233a61ed2ceSHans Rosenfeld 
4234a61ed2ceSHans Rosenfeld 	idx = ccid_minor_find_user(getminor(dev));
4235a61ed2ceSHans Rosenfeld 	if (idx == NULL) {
4236a61ed2ceSHans Rosenfeld 		return (ENOENT);
4237a61ed2ceSHans Rosenfeld 	}
4238a61ed2ceSHans Rosenfeld 
4239a61ed2ceSHans Rosenfeld 	cmp = idx->cmi_data.cmi_user;
4240a61ed2ceSHans Rosenfeld 	slot = cmp->cm_slot;
4241a61ed2ceSHans Rosenfeld 	ccid = slot->cs_ccid;
4242a61ed2ceSHans Rosenfeld 
4243a61ed2ceSHans Rosenfeld 	mutex_enter(&ccid->ccid_mutex);
4244a61ed2ceSHans Rosenfeld 	if ((ccid->ccid_flags & CCID_F_DISCONNECTED) != 0) {
4245a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
4246a61ed2ceSHans Rosenfeld 		return (ENODEV);
4247a61ed2ceSHans Rosenfeld 	}
4248a61ed2ceSHans Rosenfeld 
4249a61ed2ceSHans Rosenfeld 	if (!(cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) != 0) {
4250a61ed2ceSHans Rosenfeld 		mutex_exit(&ccid->ccid_mutex);
4251a61ed2ceSHans Rosenfeld 		return (EACCES);
4252a61ed2ceSHans Rosenfeld 	}
4253a61ed2ceSHans Rosenfeld 
4254a61ed2ceSHans Rosenfeld 	/*
4255a61ed2ceSHans Rosenfeld 	 * If the CCID_IO_F_DONE flag is set, then we're always
4256a61ed2ceSHans Rosenfeld 	 * readable. However, flags are insufficient to be writeable.
4257a61ed2ceSHans Rosenfeld 	 */
4258a61ed2ceSHans Rosenfeld 	if ((slot->cs_io.ci_flags & CCID_IO_F_DONE) != 0) {
4259a61ed2ceSHans Rosenfeld 		ready |= POLLIN | POLLRDNORM;
4260a61ed2ceSHans Rosenfeld 	} else if ((slot->cs_flags & CCID_SLOT_F_ACTIVE) != 0 &&
4261a61ed2ceSHans Rosenfeld 	    (slot->cs_io.ci_flags & CCID_IO_F_POLLOUT_FLAGS) == 0 &&
4262a61ed2ceSHans Rosenfeld 	    slot->cs_icc.icc_tx != NULL) {
4263a61ed2ceSHans Rosenfeld 		ready |= POLLOUT;
4264a61ed2ceSHans Rosenfeld 	}
4265a61ed2ceSHans Rosenfeld 
4266a61ed2ceSHans Rosenfeld 	if ((slot->cs_flags & CCID_SLOT_F_PRESENT) == 0) {
4267a61ed2ceSHans Rosenfeld 		ready |= POLLHUP;
4268a61ed2ceSHans Rosenfeld 	}
4269a61ed2ceSHans Rosenfeld 
4270a61ed2ceSHans Rosenfeld 	*reventsp = ready & events;
4271a61ed2ceSHans Rosenfeld 	if ((*reventsp == 0 && !anyyet) || (events & POLLET)) {
4272a61ed2ceSHans Rosenfeld 		*phpp = &cmp->cm_pollhead;
4273a61ed2ceSHans Rosenfeld 	}
4274a61ed2ceSHans Rosenfeld 
4275a61ed2ceSHans Rosenfeld 	mutex_exit(&ccid->ccid_mutex);
4276a61ed2ceSHans Rosenfeld 
4277a61ed2ceSHans Rosenfeld 	return (0);
4278a61ed2ceSHans Rosenfeld }
4279a61ed2ceSHans Rosenfeld 
4280a61ed2ceSHans Rosenfeld static int
ccid_close(dev_t dev,int flag,int otyp,cred_t * credp)4281a61ed2ceSHans Rosenfeld ccid_close(dev_t dev, int flag, int otyp, cred_t *credp)
4282a61ed2ceSHans Rosenfeld {
4283a61ed2ceSHans Rosenfeld 	ccid_minor_idx_t *idx;
4284a61ed2ceSHans Rosenfeld 	ccid_minor_t *cmp;
4285a61ed2ceSHans Rosenfeld 	ccid_slot_t *slot;
4286a61ed2ceSHans Rosenfeld 
4287a61ed2ceSHans Rosenfeld 	idx = ccid_minor_find_user(getminor(dev));
4288a61ed2ceSHans Rosenfeld 	if (idx == NULL) {
4289a61ed2ceSHans Rosenfeld 		return (ENOENT);
4290a61ed2ceSHans Rosenfeld 	}
4291a61ed2ceSHans Rosenfeld 
4292a61ed2ceSHans Rosenfeld 	/*
4293a61ed2ceSHans Rosenfeld 	 * First tear down the global index entry.
4294a61ed2ceSHans Rosenfeld 	 */
4295a61ed2ceSHans Rosenfeld 	cmp = idx->cmi_data.cmi_user;
4296a61ed2ceSHans Rosenfeld 	slot = cmp->cm_slot;
4297a61ed2ceSHans Rosenfeld 	ccid_minor_idx_free(idx);
4298a61ed2ceSHans Rosenfeld 
4299a61ed2ceSHans Rosenfeld 	/*
4300a61ed2ceSHans Rosenfeld 	 * If the minor node was closed without an explicit transaction end,
4301a61ed2ceSHans Rosenfeld 	 * then we need to assume that the reader's ICC is in an arbitrary
4302a61ed2ceSHans Rosenfeld 	 * state. For example, the ICC could have a specific PIV applet
4303a61ed2ceSHans Rosenfeld 	 * selected. In such a case, the only safe thing to do is to force a
4304a61ed2ceSHans Rosenfeld 	 * reset.
4305a61ed2ceSHans Rosenfeld 	 */
4306a61ed2ceSHans Rosenfeld 	mutex_enter(&slot->cs_ccid->ccid_mutex);
4307a61ed2ceSHans Rosenfeld 	if ((cmp->cm_flags & CCID_MINOR_F_HAS_EXCL) != 0) {
4308a61ed2ceSHans Rosenfeld 		cmp->cm_flags |= CCID_MINOR_F_TXN_RESET;
4309a61ed2ceSHans Rosenfeld 		ccid_slot_excl_rele(slot);
4310a61ed2ceSHans Rosenfeld 	}
4311a61ed2ceSHans Rosenfeld 
4312a61ed2ceSHans Rosenfeld 	list_remove(&slot->cs_minors, cmp);
4313a61ed2ceSHans Rosenfeld 	mutex_exit(&slot->cs_ccid->ccid_mutex);
4314a61ed2ceSHans Rosenfeld 
4315a61ed2ceSHans Rosenfeld 	pollhead_clean(&cmp->cm_pollhead);
4316a61ed2ceSHans Rosenfeld 	ccid_minor_free(cmp);
4317a61ed2ceSHans Rosenfeld 
4318a61ed2ceSHans Rosenfeld 	return (0);
4319a61ed2ceSHans Rosenfeld }
4320a61ed2ceSHans Rosenfeld 
4321a61ed2ceSHans Rosenfeld static struct cb_ops ccid_cb_ops = {
4322a61ed2ceSHans Rosenfeld 	ccid_open,		/* cb_open */
4323a61ed2ceSHans Rosenfeld 	ccid_close,		/* cb_close */
4324a61ed2ceSHans Rosenfeld 	nodev,			/* cb_strategy */
4325a61ed2ceSHans Rosenfeld 	nodev,			/* cb_print */
4326a61ed2ceSHans Rosenfeld 	nodev,			/* cb_dump */
4327a61ed2ceSHans Rosenfeld 	ccid_read,		/* cb_read */
4328a61ed2ceSHans Rosenfeld 	ccid_write,		/* cb_write */
4329a61ed2ceSHans Rosenfeld 	ccid_ioctl,		/* cb_ioctl */
4330a61ed2ceSHans Rosenfeld 	nodev,			/* cb_devmap */
4331a61ed2ceSHans Rosenfeld 	nodev,			/* cb_mmap */
4332a61ed2ceSHans Rosenfeld 	nodev,			/* cb_segmap */
4333a61ed2ceSHans Rosenfeld 	ccid_chpoll,		/* cb_chpoll */
4334a61ed2ceSHans Rosenfeld 	ddi_prop_op,		/* cb_prop_op */
4335a61ed2ceSHans Rosenfeld 	NULL,			/* cb_stream */
4336a61ed2ceSHans Rosenfeld 	D_MP,			/* cb_flag */
4337a61ed2ceSHans Rosenfeld 	CB_REV,			/* cb_rev */
4338a61ed2ceSHans Rosenfeld 	nodev,			/* cb_aread */
4339a61ed2ceSHans Rosenfeld 	nodev			/* cb_awrite */
4340a61ed2ceSHans Rosenfeld };
4341a61ed2ceSHans Rosenfeld 
4342a61ed2ceSHans Rosenfeld static struct dev_ops ccid_dev_ops = {
4343a61ed2ceSHans Rosenfeld 	DEVO_REV,		/* devo_rev */
4344a61ed2ceSHans Rosenfeld 	0,			/* devo_refcnt */
4345a61ed2ceSHans Rosenfeld 	ccid_getinfo,		/* devo_getinfo */
4346a61ed2ceSHans Rosenfeld 	nulldev,		/* devo_identify */
4347a61ed2ceSHans Rosenfeld 	nulldev,		/* devo_probe */
4348a61ed2ceSHans Rosenfeld 	ccid_attach,		/* devo_attach */
4349a61ed2ceSHans Rosenfeld 	ccid_detach,		/* devo_detach */
4350a61ed2ceSHans Rosenfeld 	nodev,			/* devo_reset */
4351a61ed2ceSHans Rosenfeld 	&ccid_cb_ops,		/* devo_cb_ops */
4352a61ed2ceSHans Rosenfeld 	NULL,			/* devo_bus_ops */
4353a61ed2ceSHans Rosenfeld 	NULL,			/* devo_power */
4354a61ed2ceSHans Rosenfeld 	ddi_quiesce_not_supported /* devo_quiesce */
4355a61ed2ceSHans Rosenfeld };
4356a61ed2ceSHans Rosenfeld 
4357a61ed2ceSHans Rosenfeld static struct modldrv ccid_modldrv = {
4358a61ed2ceSHans Rosenfeld 	&mod_driverops,
4359a61ed2ceSHans Rosenfeld 	"USB CCID",
4360a61ed2ceSHans Rosenfeld 	&ccid_dev_ops
4361a61ed2ceSHans Rosenfeld };
4362a61ed2ceSHans Rosenfeld 
4363a61ed2ceSHans Rosenfeld static struct modlinkage ccid_modlinkage = {
4364a61ed2ceSHans Rosenfeld 	MODREV_1,
4365a61ed2ceSHans Rosenfeld 	{ &ccid_modldrv, NULL }
4366a61ed2ceSHans Rosenfeld };
4367a61ed2ceSHans Rosenfeld 
4368a61ed2ceSHans Rosenfeld int
_init(void)4369a61ed2ceSHans Rosenfeld _init(void)
4370a61ed2ceSHans Rosenfeld {
4371a61ed2ceSHans Rosenfeld 	int ret;
4372a61ed2ceSHans Rosenfeld 
4373a61ed2ceSHans Rosenfeld 	if ((ret = ddi_soft_state_init(&ccid_softstate, sizeof (ccid_t),
4374a61ed2ceSHans Rosenfeld 	    0)) != 0) {
4375a61ed2ceSHans Rosenfeld 		return (ret);
4376a61ed2ceSHans Rosenfeld 	}
4377a61ed2ceSHans Rosenfeld 
4378a61ed2ceSHans Rosenfeld 	if ((ccid_minors = id_space_create("ccid_minors", CCID_MINOR_MIN,
4379a61ed2ceSHans Rosenfeld 	    INT_MAX)) == NULL) {
4380a61ed2ceSHans Rosenfeld 		ddi_soft_state_fini(&ccid_softstate);
4381a61ed2ceSHans Rosenfeld 		return (ret);
4382a61ed2ceSHans Rosenfeld 	}
4383a61ed2ceSHans Rosenfeld 
4384a61ed2ceSHans Rosenfeld 	if ((ret = mod_install(&ccid_modlinkage)) != 0) {
4385a61ed2ceSHans Rosenfeld 		id_space_destroy(ccid_minors);
4386a61ed2ceSHans Rosenfeld 		ccid_minors = NULL;
4387a61ed2ceSHans Rosenfeld 		ddi_soft_state_fini(&ccid_softstate);
4388a61ed2ceSHans Rosenfeld 		return (ret);
4389a61ed2ceSHans Rosenfeld 	}
4390a61ed2ceSHans Rosenfeld 
4391a61ed2ceSHans Rosenfeld 	mutex_init(&ccid_idxlock, NULL, MUTEX_DRIVER, NULL);
4392a61ed2ceSHans Rosenfeld 	avl_create(&ccid_idx, ccid_idx_comparator, sizeof (ccid_minor_idx_t),
4393a61ed2ceSHans Rosenfeld 	    offsetof(ccid_minor_idx_t, cmi_avl));
4394a61ed2ceSHans Rosenfeld 
4395a61ed2ceSHans Rosenfeld 	return (ret);
4396a61ed2ceSHans Rosenfeld }
4397a61ed2ceSHans Rosenfeld 
4398a61ed2ceSHans Rosenfeld int
_info(struct modinfo * modinfop)4399a61ed2ceSHans Rosenfeld _info(struct modinfo *modinfop)
4400a61ed2ceSHans Rosenfeld {
4401a61ed2ceSHans Rosenfeld 	return (mod_info(&ccid_modlinkage, modinfop));
4402a61ed2ceSHans Rosenfeld }
4403a61ed2ceSHans Rosenfeld 
4404a61ed2ceSHans Rosenfeld int
_fini(void)4405a61ed2ceSHans Rosenfeld _fini(void)
4406a61ed2ceSHans Rosenfeld {
4407a61ed2ceSHans Rosenfeld 	int ret;
4408a61ed2ceSHans Rosenfeld 
4409a61ed2ceSHans Rosenfeld 	if ((ret = mod_remove(&ccid_modlinkage)) != 0) {
4410a61ed2ceSHans Rosenfeld 		return (ret);
4411a61ed2ceSHans Rosenfeld 	}
4412a61ed2ceSHans Rosenfeld 
4413a61ed2ceSHans Rosenfeld 	avl_destroy(&ccid_idx);
4414a61ed2ceSHans Rosenfeld 	mutex_destroy(&ccid_idxlock);
4415a61ed2ceSHans Rosenfeld 	id_space_destroy(ccid_minors);
4416a61ed2ceSHans Rosenfeld 	ccid_minors = NULL;
4417a61ed2ceSHans Rosenfeld 	ddi_soft_state_fini(&ccid_softstate);
4418a61ed2ceSHans Rosenfeld 
4419a61ed2ceSHans Rosenfeld 	return (ret);
4420a61ed2ceSHans Rosenfeld }
4421