xref: /illumos-gate/usr/src/lib/libpcsc/common/libpcsc.c (revision cd9341501a54e6a19001a8d45e4ccb056a0f024e)
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.
14118b2dbfSJoshua M. Clulow  * Copyright 2022 Oxide Computer Company
15a61ed2ceSHans Rosenfeld  */
16a61ed2ceSHans Rosenfeld 
17a61ed2ceSHans Rosenfeld #include <stdlib.h>
18118b2dbfSJoshua M. Clulow #include <stdio.h>
19118b2dbfSJoshua M. Clulow #include <stdbool.h>
20a61ed2ceSHans Rosenfeld #include <sys/types.h>
21a61ed2ceSHans Rosenfeld #include <sys/stat.h>
22118b2dbfSJoshua M. Clulow #include <sys/list.h>
23a61ed2ceSHans Rosenfeld #include <fcntl.h>
24a61ed2ceSHans Rosenfeld #include <fts.h>
25a61ed2ceSHans Rosenfeld #include <errno.h>
26a61ed2ceSHans Rosenfeld #include <strings.h>
27a61ed2ceSHans Rosenfeld #include <unistd.h>
28118b2dbfSJoshua M. Clulow #include <upanic.h>
29a61ed2ceSHans Rosenfeld #include <sys/debug.h>
30a61ed2ceSHans Rosenfeld #include <sys/filio.h>
31a61ed2ceSHans Rosenfeld #include <sys/usb/clients/ccid/uccid.h>
32a61ed2ceSHans Rosenfeld 
33a61ed2ceSHans Rosenfeld #include <winscard.h>
34a61ed2ceSHans Rosenfeld 
35a61ed2ceSHans Rosenfeld /*
36a61ed2ceSHans Rosenfeld  * Implementation of the PCSC library leveraging the uccid framework.
37a61ed2ceSHans Rosenfeld  */
38a61ed2ceSHans Rosenfeld 
39a61ed2ceSHans Rosenfeld typedef struct pcsc_hdl {
40a61ed2ceSHans Rosenfeld 	hrtime_t pcsc_create_time;
41118b2dbfSJoshua M. Clulow 	list_t pcsc_autoalloc;
42118b2dbfSJoshua M. Clulow 	list_t pcsc_cards;
43a61ed2ceSHans Rosenfeld } pcsc_hdl_t;
44a61ed2ceSHans Rosenfeld 
45a61ed2ceSHans Rosenfeld typedef struct pcsc_card {
46118b2dbfSJoshua M. Clulow 	list_node_t pcc_link;
47118b2dbfSJoshua M. Clulow 	pcsc_hdl_t *pcc_hdl;
48a61ed2ceSHans Rosenfeld 	int pcc_fd;
49118b2dbfSJoshua M. Clulow 	char *pcc_name;
50118b2dbfSJoshua M. Clulow 	size_t pcc_namelen;
51a61ed2ceSHans Rosenfeld } pcsc_card_t;
52a61ed2ceSHans Rosenfeld 
53118b2dbfSJoshua M. Clulow typedef struct pcsc_mem {
54118b2dbfSJoshua M. Clulow 	list_node_t pcm_link;
55118b2dbfSJoshua M. Clulow 	void *pcm_buf;
56118b2dbfSJoshua M. Clulow } pcsc_mem_t;
57118b2dbfSJoshua M. Clulow 
58a61ed2ceSHans Rosenfeld /*
59a61ed2ceSHans Rosenfeld  * Required globals
60a61ed2ceSHans Rosenfeld  */
61a61ed2ceSHans Rosenfeld SCARD_IO_REQUEST g_rgSCardT0Pci = {
62a61ed2ceSHans Rosenfeld 	SCARD_PROTOCOL_T0,
63a61ed2ceSHans Rosenfeld 	0
64a61ed2ceSHans Rosenfeld };
65a61ed2ceSHans Rosenfeld 
66a61ed2ceSHans Rosenfeld SCARD_IO_REQUEST g_rgSCardT1Pci = {
67a61ed2ceSHans Rosenfeld 	SCARD_PROTOCOL_T1,
68a61ed2ceSHans Rosenfeld 	0
69a61ed2ceSHans Rosenfeld };
70a61ed2ceSHans Rosenfeld 
71a61ed2ceSHans Rosenfeld SCARD_IO_REQUEST g_rgSCardRawPci = {
72a61ed2ceSHans Rosenfeld 	SCARD_PROTOCOL_RAW,
73a61ed2ceSHans Rosenfeld 	0
74a61ed2ceSHans Rosenfeld };
75a61ed2ceSHans Rosenfeld 
76a61ed2ceSHans Rosenfeld const char *
pcsc_stringify_error(const LONG err)77a61ed2ceSHans Rosenfeld pcsc_stringify_error(const LONG err)
78a61ed2ceSHans Rosenfeld {
79a61ed2ceSHans Rosenfeld 	switch (err) {
80a61ed2ceSHans Rosenfeld 	case SCARD_S_SUCCESS:
81a61ed2ceSHans Rosenfeld 		return ("no error");
82a61ed2ceSHans Rosenfeld 	case SCARD_F_INTERNAL_ERROR:
83a61ed2ceSHans Rosenfeld 		return ("internal error");
84a61ed2ceSHans Rosenfeld 	case SCARD_E_CANCELLED:
85a61ed2ceSHans Rosenfeld 		return ("request cancelled");
86a61ed2ceSHans Rosenfeld 	case SCARD_E_INVALID_HANDLE:
87a61ed2ceSHans Rosenfeld 		return ("invalid handle");
88a61ed2ceSHans Rosenfeld 	case SCARD_E_INVALID_PARAMETER:
89a61ed2ceSHans Rosenfeld 		return ("invalid parameter");
90a61ed2ceSHans Rosenfeld 	case SCARD_E_NO_MEMORY:
91a61ed2ceSHans Rosenfeld 		return ("no memory");
92a61ed2ceSHans Rosenfeld 	case SCARD_E_INSUFFICIENT_BUFFER:
93a61ed2ceSHans Rosenfeld 		return ("buffer was insufficiently sized");
94a61ed2ceSHans Rosenfeld 	case SCARD_E_INVALID_VALUE:
95a61ed2ceSHans Rosenfeld 		return ("invalid value passed");
96a61ed2ceSHans Rosenfeld 	case SCARD_E_UNKNOWN_READER:
97a61ed2ceSHans Rosenfeld 		return ("unknown reader");
98a61ed2ceSHans Rosenfeld 	case SCARD_E_TIMEOUT:
99a61ed2ceSHans Rosenfeld 		return ("timeout occurred");
100a61ed2ceSHans Rosenfeld 	case SCARD_E_SHARING_VIOLATION:
101a61ed2ceSHans Rosenfeld 		return ("sharing violation");
102a61ed2ceSHans Rosenfeld 	case SCARD_E_NO_SMARTCARD:
103a61ed2ceSHans Rosenfeld 		return ("no smartcard present");
104a61ed2ceSHans Rosenfeld 	case SCARD_E_UNKNOWN_CARD:
105a61ed2ceSHans Rosenfeld 		return ("unknown ICC");
106a61ed2ceSHans Rosenfeld 	case SCARD_E_PROTO_MISMATCH:
107a61ed2ceSHans Rosenfeld 		return ("protocol mismatch");
108a61ed2ceSHans Rosenfeld 	case SCARD_F_COMM_ERROR:
109a61ed2ceSHans Rosenfeld 		return ("communication error");
110a61ed2ceSHans Rosenfeld 	case SCARD_F_UNKNOWN_ERROR:
111a61ed2ceSHans Rosenfeld 		return ("unknown error");
112a61ed2ceSHans Rosenfeld 	case SCARD_E_READER_UNAVAILABLE:
113a61ed2ceSHans Rosenfeld 		return ("reader unavailable");
114a61ed2ceSHans Rosenfeld 	case SCARD_E_NO_SERVICE:
115a61ed2ceSHans Rosenfeld 		return ("service error");
116a61ed2ceSHans Rosenfeld 	case SCARD_E_UNSUPPORTED_FEATURE:
117a61ed2ceSHans Rosenfeld 		return ("ICC requires unsupported feature");
118a61ed2ceSHans Rosenfeld 	case SCARD_E_NO_READERS_AVAILABLE:
119a61ed2ceSHans Rosenfeld 		return ("no readers avaiable");
120a61ed2ceSHans Rosenfeld 	case SCARD_W_UNSUPPORTED_CARD:
121a61ed2ceSHans Rosenfeld 		return ("ICC unsupported");
122a61ed2ceSHans Rosenfeld 	case SCARD_W_UNPOWERED_CARD:
123a61ed2ceSHans Rosenfeld 		return ("ICC is not powered");
124a61ed2ceSHans Rosenfeld 	case SCARD_W_RESET_CARD:
125a61ed2ceSHans Rosenfeld 		return ("ICC was reset");
126a61ed2ceSHans Rosenfeld 	case SCARD_W_REMOVED_CARD:
127a61ed2ceSHans Rosenfeld 		return ("ICC has been removed");
128a61ed2ceSHans Rosenfeld 	default:
129a61ed2ceSHans Rosenfeld 		return ("unknown error");
130a61ed2ceSHans Rosenfeld 	}
131a61ed2ceSHans Rosenfeld }
132a61ed2ceSHans Rosenfeld 
133118b2dbfSJoshua M. Clulow /*
134118b2dbfSJoshua M. Clulow  * Allocate a buffer of size "len" for use with an SCARD_AUTOALLOCATE
135118b2dbfSJoshua M. Clulow  * parameter.  Each automatically allocated buffer must be appended to the
136118b2dbfSJoshua M. Clulow  * context buffer list so that it can be freed during the call to
137118b2dbfSJoshua M. Clulow  * SCardReleaseContext().
138118b2dbfSJoshua M. Clulow  */
139118b2dbfSJoshua M. Clulow static void *
pcsc_mem_alloc(pcsc_hdl_t * hdl,size_t len)140118b2dbfSJoshua M. Clulow pcsc_mem_alloc(pcsc_hdl_t *hdl, size_t len)
141118b2dbfSJoshua M. Clulow {
142118b2dbfSJoshua M. Clulow 	pcsc_mem_t *mem;
143118b2dbfSJoshua M. Clulow 
144118b2dbfSJoshua M. Clulow 	if ((mem = malloc(sizeof (*mem))) == NULL) {
145118b2dbfSJoshua M. Clulow 		return (NULL);
146118b2dbfSJoshua M. Clulow 	}
147118b2dbfSJoshua M. Clulow 
148118b2dbfSJoshua M. Clulow 	if ((mem->pcm_buf = malloc(len)) == NULL) {
149118b2dbfSJoshua M. Clulow 		free(mem);
150118b2dbfSJoshua M. Clulow 		return (NULL);
151118b2dbfSJoshua M. Clulow 	}
152118b2dbfSJoshua M. Clulow 	list_link_init(&mem->pcm_link);
153118b2dbfSJoshua M. Clulow 
154118b2dbfSJoshua M. Clulow 	/*
155118b2dbfSJoshua M. Clulow 	 * Put the buffer on the per-context list:
156118b2dbfSJoshua M. Clulow 	 */
157118b2dbfSJoshua M. Clulow 	list_insert_tail(&hdl->pcsc_autoalloc, mem);
158118b2dbfSJoshua M. Clulow 
159118b2dbfSJoshua M. Clulow 	return (mem->pcm_buf);
160118b2dbfSJoshua M. Clulow }
161118b2dbfSJoshua M. Clulow 
162118b2dbfSJoshua M. Clulow static void
pcsc_mem_free(pcsc_hdl_t * hdl,void * buf)163118b2dbfSJoshua M. Clulow pcsc_mem_free(pcsc_hdl_t *hdl, void *buf)
164118b2dbfSJoshua M. Clulow {
165118b2dbfSJoshua M. Clulow 	for (pcsc_mem_t *mem = list_head(&hdl->pcsc_autoalloc); mem != NULL;
166118b2dbfSJoshua M. Clulow 	    mem = list_next(&hdl->pcsc_autoalloc, mem)) {
167118b2dbfSJoshua M. Clulow 		if (mem->pcm_buf == buf) {
168118b2dbfSJoshua M. Clulow 			list_remove(&hdl->pcsc_autoalloc, mem);
169118b2dbfSJoshua M. Clulow 			free(mem->pcm_buf);
170118b2dbfSJoshua M. Clulow 			free(mem);
171118b2dbfSJoshua M. Clulow 			return;
172118b2dbfSJoshua M. Clulow 		}
173118b2dbfSJoshua M. Clulow 	}
174118b2dbfSJoshua M. Clulow 
175118b2dbfSJoshua M. Clulow 	char msg[512];
176118b2dbfSJoshua M. Clulow 	(void) snprintf(msg, sizeof (msg), "freed buffer %p not in context %p",
177118b2dbfSJoshua M. Clulow 	    buf, hdl);
178118b2dbfSJoshua M. Clulow 	upanic(msg, strlen(msg));
179118b2dbfSJoshua M. Clulow }
180118b2dbfSJoshua M. Clulow 
181118b2dbfSJoshua M. Clulow static pcsc_card_t *
pcsc_card_alloc(pcsc_hdl_t * hdl,const char * reader)182118b2dbfSJoshua M. Clulow pcsc_card_alloc(pcsc_hdl_t *hdl, const char *reader)
183118b2dbfSJoshua M. Clulow {
184118b2dbfSJoshua M. Clulow 	pcsc_card_t *card;
185118b2dbfSJoshua M. Clulow 
186118b2dbfSJoshua M. Clulow 	if ((card = malloc(sizeof (*card))) == NULL) {
187118b2dbfSJoshua M. Clulow 		return (NULL);
188118b2dbfSJoshua M. Clulow 	}
189118b2dbfSJoshua M. Clulow 	card->pcc_hdl = hdl;
190118b2dbfSJoshua M. Clulow 	card->pcc_fd = -1;
191118b2dbfSJoshua M. Clulow 	list_link_init(&card->pcc_link);
192118b2dbfSJoshua M. Clulow 
193118b2dbfSJoshua M. Clulow 	/*
194118b2dbfSJoshua M. Clulow 	 * The reader name is returned as a multi-string, which means we need
195118b2dbfSJoshua M. Clulow 	 * the regular C string and then an additional null termination byte to
196118b2dbfSJoshua M. Clulow 	 * end the list of strings:
197118b2dbfSJoshua M. Clulow 	 */
198118b2dbfSJoshua M. Clulow 	card->pcc_namelen = strlen(reader) + 2;
199118b2dbfSJoshua M. Clulow 	if ((card->pcc_name = malloc(card->pcc_namelen)) == NULL) {
200118b2dbfSJoshua M. Clulow 		free(card);
201118b2dbfSJoshua M. Clulow 		return (NULL);
202118b2dbfSJoshua M. Clulow 	}
203118b2dbfSJoshua M. Clulow 	bcopy(reader, card->pcc_name, card->pcc_namelen - 1);
204118b2dbfSJoshua M. Clulow 	card->pcc_name[card->pcc_namelen - 1] = '\0';
205118b2dbfSJoshua M. Clulow 
206118b2dbfSJoshua M. Clulow 	/*
207118b2dbfSJoshua M. Clulow 	 * Insert the card handle into the per-context list so that we can free
208118b2dbfSJoshua M. Clulow 	 * them later during SCardReleaseContext().
209118b2dbfSJoshua M. Clulow 	 */
210118b2dbfSJoshua M. Clulow 	list_insert_tail(&hdl->pcsc_cards, card);
211118b2dbfSJoshua M. Clulow 
212118b2dbfSJoshua M. Clulow 	return (card);
213118b2dbfSJoshua M. Clulow }
214118b2dbfSJoshua M. Clulow 
215118b2dbfSJoshua M. Clulow static void
pcsc_card_free(pcsc_card_t * card)216118b2dbfSJoshua M. Clulow pcsc_card_free(pcsc_card_t *card)
217118b2dbfSJoshua M. Clulow {
218118b2dbfSJoshua M. Clulow 	if (card == NULL) {
219118b2dbfSJoshua M. Clulow 		return;
220118b2dbfSJoshua M. Clulow 	}
221118b2dbfSJoshua M. Clulow 
222118b2dbfSJoshua M. Clulow 	if (card->pcc_fd >= 0) {
223118b2dbfSJoshua M. Clulow 		(void) close(card->pcc_fd);
224118b2dbfSJoshua M. Clulow 	}
225118b2dbfSJoshua M. Clulow 
226118b2dbfSJoshua M. Clulow 	/*
227118b2dbfSJoshua M. Clulow 	 * Remove the card handle from the per-context list:
228118b2dbfSJoshua M. Clulow 	 */
229118b2dbfSJoshua M. Clulow 	pcsc_hdl_t *hdl = card->pcc_hdl;
230118b2dbfSJoshua M. Clulow 	list_remove(&hdl->pcsc_cards, card);
231118b2dbfSJoshua M. Clulow 
232118b2dbfSJoshua M. Clulow 	free(card->pcc_name);
233118b2dbfSJoshua M. Clulow 	free(card);
234118b2dbfSJoshua M. Clulow }
235a61ed2ceSHans Rosenfeld 
236a61ed2ceSHans Rosenfeld /*
237a61ed2ceSHans Rosenfeld  * This is called when a caller wishes to open a new Library context.
238a61ed2ceSHans Rosenfeld  */
239a61ed2ceSHans Rosenfeld LONG
SCardEstablishContext(DWORD scope,LPCVOID unused0,LPCVOID unused1,LPSCARDCONTEXT outp)240a61ed2ceSHans Rosenfeld SCardEstablishContext(DWORD scope, LPCVOID unused0, LPCVOID unused1,
241a61ed2ceSHans Rosenfeld     LPSCARDCONTEXT outp)
242a61ed2ceSHans Rosenfeld {
243a61ed2ceSHans Rosenfeld 	pcsc_hdl_t *hdl;
244a61ed2ceSHans Rosenfeld 
245a61ed2ceSHans Rosenfeld 	if (outp == NULL) {
246a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_PARAMETER);
247a61ed2ceSHans Rosenfeld 	}
248a61ed2ceSHans Rosenfeld 
249a61ed2ceSHans Rosenfeld 	if (scope != SCARD_SCOPE_SYSTEM) {
250a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
251a61ed2ceSHans Rosenfeld 	}
252a61ed2ceSHans Rosenfeld 
253a61ed2ceSHans Rosenfeld 	hdl = calloc(1, sizeof (pcsc_hdl_t));
254a61ed2ceSHans Rosenfeld 	if (hdl == NULL) {
255a61ed2ceSHans Rosenfeld 		return (SCARD_E_NO_MEMORY);
256a61ed2ceSHans Rosenfeld 	}
257118b2dbfSJoshua M. Clulow 	list_create(&hdl->pcsc_autoalloc, sizeof (pcsc_mem_t),
258118b2dbfSJoshua M. Clulow 	    offsetof(pcsc_mem_t, pcm_link));
259118b2dbfSJoshua M. Clulow 	list_create(&hdl->pcsc_cards, sizeof (pcsc_card_t),
260118b2dbfSJoshua M. Clulow 	    offsetof(pcsc_card_t, pcc_link));
261a61ed2ceSHans Rosenfeld 
262a61ed2ceSHans Rosenfeld 	hdl->pcsc_create_time = gethrtime();
263a61ed2ceSHans Rosenfeld 	*outp = hdl;
264a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
265a61ed2ceSHans Rosenfeld }
266a61ed2ceSHans Rosenfeld 
267118b2dbfSJoshua M. Clulow bool
pcsc_valid_context(SCARDCONTEXT hdl)268118b2dbfSJoshua M. Clulow pcsc_valid_context(SCARDCONTEXT hdl)
269118b2dbfSJoshua M. Clulow {
270118b2dbfSJoshua M. Clulow 	/*
271118b2dbfSJoshua M. Clulow 	 * On some other platforms, the context handle is a signed integer.
272118b2dbfSJoshua M. Clulow 	 * Some software has been observed to use -1 as an invalid handle
273118b2dbfSJoshua M. Clulow 	 * sentinel value, so we need to explicitly handle that here.
274118b2dbfSJoshua M. Clulow 	 */
275118b2dbfSJoshua M. Clulow 	return (hdl != NULL && (uintptr_t)hdl != UINTPTR_MAX);
276118b2dbfSJoshua M. Clulow }
277118b2dbfSJoshua M. Clulow 
278118b2dbfSJoshua M. Clulow LONG
SCardIsValidContext(SCARDCONTEXT hdl)279118b2dbfSJoshua M. Clulow SCardIsValidContext(SCARDCONTEXT hdl)
280118b2dbfSJoshua M. Clulow {
281118b2dbfSJoshua M. Clulow 	if (!pcsc_valid_context(hdl)) {
282118b2dbfSJoshua M. Clulow 		return (SCARD_E_INVALID_HANDLE);
283118b2dbfSJoshua M. Clulow 	}
284118b2dbfSJoshua M. Clulow 
285118b2dbfSJoshua M. Clulow 	return (SCARD_S_SUCCESS);
286118b2dbfSJoshua M. Clulow }
287118b2dbfSJoshua M. Clulow 
288a61ed2ceSHans Rosenfeld /*
289a61ed2ceSHans Rosenfeld  * This is called to free a library context from a client.
290a61ed2ceSHans Rosenfeld  */
291a61ed2ceSHans Rosenfeld LONG
SCardReleaseContext(SCARDCONTEXT arg)292118b2dbfSJoshua M. Clulow SCardReleaseContext(SCARDCONTEXT arg)
293a61ed2ceSHans Rosenfeld {
294118b2dbfSJoshua M. Clulow 	if (!pcsc_valid_context(arg)) {
295118b2dbfSJoshua M. Clulow 		return (SCARD_E_INVALID_HANDLE);
296118b2dbfSJoshua M. Clulow 	}
297118b2dbfSJoshua M. Clulow 
298118b2dbfSJoshua M. Clulow 	/*
299118b2dbfSJoshua M. Clulow 	 * Free any SCARD_AUTOALLOCATE memory now.
300118b2dbfSJoshua M. Clulow 	 */
301118b2dbfSJoshua M. Clulow 	pcsc_hdl_t *hdl = arg;
302118b2dbfSJoshua M. Clulow 	pcsc_mem_t *mem;
303118b2dbfSJoshua M. Clulow 	while ((mem = list_head(&hdl->pcsc_autoalloc)) != NULL) {
304118b2dbfSJoshua M. Clulow 		pcsc_mem_free(hdl, mem->pcm_buf);
305118b2dbfSJoshua M. Clulow 	}
306118b2dbfSJoshua M. Clulow 	list_destroy(&hdl->pcsc_autoalloc);
307118b2dbfSJoshua M. Clulow 
308118b2dbfSJoshua M. Clulow 	/*
309118b2dbfSJoshua M. Clulow 	 * Free any card handles that were not explicitly freed:
310118b2dbfSJoshua M. Clulow 	 */
311118b2dbfSJoshua M. Clulow 	pcsc_card_t *card;
312118b2dbfSJoshua M. Clulow 	while ((card = list_head(&hdl->pcsc_cards)) != NULL) {
313118b2dbfSJoshua M. Clulow 		pcsc_card_free(card);
314118b2dbfSJoshua M. Clulow 	}
315118b2dbfSJoshua M. Clulow 	list_destroy(&hdl->pcsc_cards);
316118b2dbfSJoshua M. Clulow 
317a61ed2ceSHans Rosenfeld 	free(hdl);
318a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
319a61ed2ceSHans Rosenfeld }
320a61ed2ceSHans Rosenfeld 
321a61ed2ceSHans Rosenfeld /*
322a61ed2ceSHans Rosenfeld  * This is called to release memory allocated by the library. No, it doesn't
323a61ed2ceSHans Rosenfeld  * make sense to take a const pointer when being given memory to free. It just
324a61ed2ceSHans Rosenfeld  * means we have to cast it, but remember: this isn't our API.
325a61ed2ceSHans Rosenfeld  */
326a61ed2ceSHans Rosenfeld LONG
SCardFreeMemory(SCARDCONTEXT hdl,LPCVOID mem)327118b2dbfSJoshua M. Clulow SCardFreeMemory(SCARDCONTEXT hdl, LPCVOID mem)
328a61ed2ceSHans Rosenfeld {
329118b2dbfSJoshua M. Clulow 	if (!pcsc_valid_context(hdl)) {
330118b2dbfSJoshua M. Clulow 		return (SCARD_E_INVALID_HANDLE);
331118b2dbfSJoshua M. Clulow 	}
332118b2dbfSJoshua M. Clulow 
333118b2dbfSJoshua M. Clulow 	pcsc_mem_free(hdl, (void *)mem);
334a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
335a61ed2ceSHans Rosenfeld }
336a61ed2ceSHans Rosenfeld 
337a61ed2ceSHans Rosenfeld /*
338a61ed2ceSHans Rosenfeld  * This is called by a caller to get a list of readers that exist in the system.
339a61ed2ceSHans Rosenfeld  * If lenp is set to SCARD_AUTOALLOCATE, then we are responsible for dealing
340a61ed2ceSHans Rosenfeld  * with this memory.
341a61ed2ceSHans Rosenfeld  */
342a61ed2ceSHans Rosenfeld LONG
SCardListReaders(SCARDCONTEXT arg,LPCSTR groups,LPSTR bufp,LPDWORD lenp)343118b2dbfSJoshua M. Clulow SCardListReaders(SCARDCONTEXT arg, LPCSTR groups, LPSTR bufp, LPDWORD lenp)
344a61ed2ceSHans Rosenfeld {
345118b2dbfSJoshua M. Clulow 	pcsc_hdl_t *hdl = arg;
346a61ed2ceSHans Rosenfeld 	FTS *fts;
347a61ed2ceSHans Rosenfeld 	FTSENT *ent;
348a61ed2ceSHans Rosenfeld 	char *const root[] = { "/dev/ccid", NULL };
349a61ed2ceSHans Rosenfeld 	char *ubuf;
350a61ed2ceSHans Rosenfeld 	char **readers;
351a61ed2ceSHans Rosenfeld 	uint32_t len, ulen, npaths, nalloc, off, i;
352a61ed2ceSHans Rosenfeld 	int ret;
353a61ed2ceSHans Rosenfeld 
354118b2dbfSJoshua M. Clulow 	if (!pcsc_valid_context(hdl)) {
355118b2dbfSJoshua M. Clulow 		return (SCARD_E_INVALID_HANDLE);
356118b2dbfSJoshua M. Clulow 	}
357118b2dbfSJoshua M. Clulow 
358a61ed2ceSHans Rosenfeld 	if (groups != NULL || lenp == NULL) {
359a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_PARAMETER);
360a61ed2ceSHans Rosenfeld 	}
361a61ed2ceSHans Rosenfeld 
362a61ed2ceSHans Rosenfeld 	fts = fts_open(root, FTS_LOGICAL | FTS_NOCHDIR, NULL);
363a61ed2ceSHans Rosenfeld 	if (fts == NULL) {
364a61ed2ceSHans Rosenfeld 		switch (errno) {
365a61ed2ceSHans Rosenfeld 		case ENOENT:
366a61ed2ceSHans Rosenfeld 		case ENOTDIR:
367a61ed2ceSHans Rosenfeld 			return (SCARD_E_NO_READERS_AVAILABLE);
368a61ed2ceSHans Rosenfeld 		case ENOMEM:
369a61ed2ceSHans Rosenfeld 		case EAGAIN:
370a61ed2ceSHans Rosenfeld 			return (SCARD_E_NO_MEMORY);
371a61ed2ceSHans Rosenfeld 		default:
372a61ed2ceSHans Rosenfeld 			return (SCARD_E_NO_SERVICE);
373a61ed2ceSHans Rosenfeld 		}
374a61ed2ceSHans Rosenfeld 	}
375a61ed2ceSHans Rosenfeld 
376a61ed2ceSHans Rosenfeld 	npaths = nalloc = 0;
377a61ed2ceSHans Rosenfeld 	/*
378a61ed2ceSHans Rosenfeld 	 * Account for the NUL we'll have to place at the end of this.
379a61ed2ceSHans Rosenfeld 	 */
380a61ed2ceSHans Rosenfeld 	len = 1;
381a61ed2ceSHans Rosenfeld 	readers = NULL;
382a61ed2ceSHans Rosenfeld 	while ((ent = fts_read(fts)) != NULL) {
383a61ed2ceSHans Rosenfeld 		size_t plen;
384a61ed2ceSHans Rosenfeld 
385a61ed2ceSHans Rosenfeld 		if (ent->fts_level != 2 || ent->fts_info == FTS_DP)
386a61ed2ceSHans Rosenfeld 			continue;
387a61ed2ceSHans Rosenfeld 
388a61ed2ceSHans Rosenfeld 		if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS)
389a61ed2ceSHans Rosenfeld 			continue;
390a61ed2ceSHans Rosenfeld 
391a61ed2ceSHans Rosenfeld 		if (S_ISCHR(ent->fts_statp->st_mode) == 0)
392a61ed2ceSHans Rosenfeld 			continue;
393a61ed2ceSHans Rosenfeld 
394a61ed2ceSHans Rosenfeld 		plen = strlen(ent->fts_path) + 1;
395a61ed2ceSHans Rosenfeld 		if (UINT32_MAX - len <= plen) {
396a61ed2ceSHans Rosenfeld 			/*
397a61ed2ceSHans Rosenfeld 			 * I mean, it's true. But I wish I could just give you
398a61ed2ceSHans Rosenfeld 			 * EOVERFLOW.
399a61ed2ceSHans Rosenfeld 			 */
400a61ed2ceSHans Rosenfeld 			ret = SCARD_E_INSUFFICIENT_BUFFER;
401a61ed2ceSHans Rosenfeld 			goto out;
402a61ed2ceSHans Rosenfeld 		}
403a61ed2ceSHans Rosenfeld 
404a61ed2ceSHans Rosenfeld 		if (npaths == nalloc) {
405a61ed2ceSHans Rosenfeld 			char **tmp;
406a61ed2ceSHans Rosenfeld 
407a61ed2ceSHans Rosenfeld 			nalloc += 8;
408a61ed2ceSHans Rosenfeld 			tmp = reallocarray(readers, nalloc, sizeof (char *));
409a61ed2ceSHans Rosenfeld 			if (tmp == NULL) {
410a61ed2ceSHans Rosenfeld 				ret = SCARD_E_NO_MEMORY;
411a61ed2ceSHans Rosenfeld 				goto out;
412a61ed2ceSHans Rosenfeld 			}
413a61ed2ceSHans Rosenfeld 			readers = tmp;
414a61ed2ceSHans Rosenfeld 		}
415a61ed2ceSHans Rosenfeld 		readers[npaths] = strdup(ent->fts_path);
416a61ed2ceSHans Rosenfeld 		npaths++;
417a61ed2ceSHans Rosenfeld 		len += plen;
418a61ed2ceSHans Rosenfeld 	}
419a61ed2ceSHans Rosenfeld 
420a61ed2ceSHans Rosenfeld 	if (npaths == 0) {
421a61ed2ceSHans Rosenfeld 		ret = SCARD_E_NO_READERS_AVAILABLE;
422a61ed2ceSHans Rosenfeld 		goto out;
423a61ed2ceSHans Rosenfeld 	}
424a61ed2ceSHans Rosenfeld 
425a61ed2ceSHans Rosenfeld 	ulen = *lenp;
426a61ed2ceSHans Rosenfeld 	*lenp = len;
427a61ed2ceSHans Rosenfeld 	if (ulen != SCARD_AUTOALLOCATE) {
428a61ed2ceSHans Rosenfeld 		if (bufp == NULL) {
429a61ed2ceSHans Rosenfeld 			ret = SCARD_S_SUCCESS;
430a61ed2ceSHans Rosenfeld 			goto out;
431a61ed2ceSHans Rosenfeld 		}
432a61ed2ceSHans Rosenfeld 
433a61ed2ceSHans Rosenfeld 		if (ulen < len) {
434a61ed2ceSHans Rosenfeld 			ret = SCARD_E_INSUFFICIENT_BUFFER;
435a61ed2ceSHans Rosenfeld 			goto out;
436a61ed2ceSHans Rosenfeld 		}
437a61ed2ceSHans Rosenfeld 
438a61ed2ceSHans Rosenfeld 		ubuf = bufp;
439a61ed2ceSHans Rosenfeld 	} else {
440a61ed2ceSHans Rosenfeld 		char **bufpp;
441a61ed2ceSHans Rosenfeld 		if (bufp == NULL) {
442a61ed2ceSHans Rosenfeld 			ret = SCARD_E_INVALID_PARAMETER;
443a61ed2ceSHans Rosenfeld 			goto out;
444a61ed2ceSHans Rosenfeld 		}
445a61ed2ceSHans Rosenfeld 
446118b2dbfSJoshua M. Clulow 		if ((ubuf = pcsc_mem_alloc(hdl, ulen)) == NULL) {
447a61ed2ceSHans Rosenfeld 			ret = SCARD_E_NO_MEMORY;
448a61ed2ceSHans Rosenfeld 			goto out;
449a61ed2ceSHans Rosenfeld 		}
450a61ed2ceSHans Rosenfeld 
451a61ed2ceSHans Rosenfeld 		bufpp = (void *)bufp;
452a61ed2ceSHans Rosenfeld 		*bufpp = ubuf;
453a61ed2ceSHans Rosenfeld 	}
454a61ed2ceSHans Rosenfeld 	ret = SCARD_S_SUCCESS;
455a61ed2ceSHans Rosenfeld 
456a61ed2ceSHans Rosenfeld 	for (off = 0, i = 0; i < npaths; i++) {
457a61ed2ceSHans Rosenfeld 		size_t slen = strlen(readers[i]) + 1;
458a61ed2ceSHans Rosenfeld 		bcopy(readers[i], ubuf + off, slen);
459a61ed2ceSHans Rosenfeld 		off += slen;
460a61ed2ceSHans Rosenfeld 		VERIFY3U(off, <=, len);
461a61ed2ceSHans Rosenfeld 	}
462a61ed2ceSHans Rosenfeld 	VERIFY3U(off, ==, len - 1);
463a61ed2ceSHans Rosenfeld 	ubuf[off] = '\0';
464a61ed2ceSHans Rosenfeld out:
465a61ed2ceSHans Rosenfeld 	for (i = 0; i < npaths; i++) {
466a61ed2ceSHans Rosenfeld 		free(readers[i]);
467a61ed2ceSHans Rosenfeld 	}
468a61ed2ceSHans Rosenfeld 	free(readers);
469a61ed2ceSHans Rosenfeld 	(void) fts_close(fts);
470a61ed2ceSHans Rosenfeld 	return (ret);
471a61ed2ceSHans Rosenfeld }
472a61ed2ceSHans Rosenfeld 
473a61ed2ceSHans Rosenfeld static LONG
uccid_status_helper(int fd,DWORD prots,uccid_cmd_status_t * ucs)474a61ed2ceSHans Rosenfeld uccid_status_helper(int fd, DWORD prots, uccid_cmd_status_t *ucs)
475a61ed2ceSHans Rosenfeld {
476a61ed2ceSHans Rosenfeld 	/*
477a61ed2ceSHans Rosenfeld 	 * Get the status of this slot and find out information about the slot.
478a61ed2ceSHans Rosenfeld 	 * We need to see if there's an ICC present and if it matches the
479a61ed2ceSHans Rosenfeld 	 * current protocol. If not, then we have to fail this.
480a61ed2ceSHans Rosenfeld 	 */
481a61ed2ceSHans Rosenfeld 	bzero(ucs, sizeof (uccid_cmd_status_t));
482a61ed2ceSHans Rosenfeld 	ucs->ucs_version = UCCID_CURRENT_VERSION;
483a61ed2ceSHans Rosenfeld 	if (ioctl(fd, UCCID_CMD_STATUS, ucs) != 0) {
484a61ed2ceSHans Rosenfeld 		return (SCARD_F_UNKNOWN_ERROR);
485a61ed2ceSHans Rosenfeld 	}
486a61ed2ceSHans Rosenfeld 
487a61ed2ceSHans Rosenfeld 	if ((ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT) == 0) {
488a61ed2ceSHans Rosenfeld 		return (SCARD_W_REMOVED_CARD);
489a61ed2ceSHans Rosenfeld 	}
490a61ed2ceSHans Rosenfeld 
491a61ed2ceSHans Rosenfeld 	if ((ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) == 0) {
492a61ed2ceSHans Rosenfeld 		return (SCARD_W_UNPOWERED_CARD);
493a61ed2ceSHans Rosenfeld 	}
494a61ed2ceSHans Rosenfeld 
495a61ed2ceSHans Rosenfeld 	if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) == 0) {
496a61ed2ceSHans Rosenfeld 		return (SCARD_W_UNSUPPORTED_CARD);
497a61ed2ceSHans Rosenfeld 	}
498a61ed2ceSHans Rosenfeld 
499a61ed2ceSHans Rosenfeld 	if ((ucs->ucs_prot & prots) == 0) {
500a61ed2ceSHans Rosenfeld 		return (SCARD_E_PROTO_MISMATCH);
501a61ed2ceSHans Rosenfeld 	}
502a61ed2ceSHans Rosenfeld 
503a61ed2ceSHans Rosenfeld 	return (0);
504a61ed2ceSHans Rosenfeld }
505a61ed2ceSHans Rosenfeld 
506a61ed2ceSHans Rosenfeld LONG
SCardConnect(SCARDCONTEXT hdl,LPCSTR reader,DWORD mode,DWORD prots,LPSCARDHANDLE iccp,LPDWORD protp)507a61ed2ceSHans Rosenfeld SCardConnect(SCARDCONTEXT hdl, LPCSTR reader, DWORD mode, DWORD prots,
508a61ed2ceSHans Rosenfeld     LPSCARDHANDLE iccp, LPDWORD protp)
509a61ed2ceSHans Rosenfeld {
510a61ed2ceSHans Rosenfeld 	LONG ret;
511a61ed2ceSHans Rosenfeld 	uccid_cmd_status_t ucs;
512a61ed2ceSHans Rosenfeld 	pcsc_card_t *card;
513a61ed2ceSHans Rosenfeld 
514118b2dbfSJoshua M. Clulow 	if (!pcsc_valid_context(hdl)) {
515118b2dbfSJoshua M. Clulow 		return (SCARD_E_INVALID_HANDLE);
516118b2dbfSJoshua M. Clulow 	}
517118b2dbfSJoshua M. Clulow 
518a61ed2ceSHans Rosenfeld 	if (reader == NULL) {
519a61ed2ceSHans Rosenfeld 		return (SCARD_E_UNKNOWN_READER);
520a61ed2ceSHans Rosenfeld 	}
521a61ed2ceSHans Rosenfeld 
522a61ed2ceSHans Rosenfeld 	if (iccp == NULL || protp == NULL) {
523a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_PARAMETER);
524a61ed2ceSHans Rosenfeld 	}
525a61ed2ceSHans Rosenfeld 
526a61ed2ceSHans Rosenfeld 	if (mode != SCARD_SHARE_SHARED) {
527a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
528a61ed2ceSHans Rosenfeld 	}
529a61ed2ceSHans Rosenfeld 
530a61ed2ceSHans Rosenfeld 	if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
531a61ed2ceSHans Rosenfeld 	    SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
532a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
533a61ed2ceSHans Rosenfeld 	}
534a61ed2ceSHans Rosenfeld 
535a61ed2ceSHans Rosenfeld 	if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
536a61ed2ceSHans Rosenfeld 		return (SCARD_E_UNSUPPORTED_FEATURE);
537a61ed2ceSHans Rosenfeld 	}
538a61ed2ceSHans Rosenfeld 
539118b2dbfSJoshua M. Clulow 	if ((card = pcsc_card_alloc(hdl, reader)) == NULL) {
540118b2dbfSJoshua M. Clulow 		pcsc_card_free(card);
541a61ed2ceSHans Rosenfeld 		return (SCARD_E_NO_MEMORY);
542a61ed2ceSHans Rosenfeld 	}
543a61ed2ceSHans Rosenfeld 
544a61ed2ceSHans Rosenfeld 	if ((card->pcc_fd = open(reader, O_RDWR)) < 0) {
545118b2dbfSJoshua M. Clulow 		pcsc_card_free(card);
546a61ed2ceSHans Rosenfeld 		switch (errno) {
547a61ed2ceSHans Rosenfeld 		case ENOENT:
548a61ed2ceSHans Rosenfeld 			return (SCARD_E_UNKNOWN_READER);
549a61ed2ceSHans Rosenfeld 		default:
550a61ed2ceSHans Rosenfeld 			return (SCARD_F_UNKNOWN_ERROR);
551a61ed2ceSHans Rosenfeld 		}
552a61ed2ceSHans Rosenfeld 	}
553a61ed2ceSHans Rosenfeld 
554118b2dbfSJoshua M. Clulow 	if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0) {
555118b2dbfSJoshua M. Clulow 		pcsc_card_free(card);
556118b2dbfSJoshua M. Clulow 		return (ret);
557118b2dbfSJoshua M. Clulow 	}
558a61ed2ceSHans Rosenfeld 
559a61ed2ceSHans Rosenfeld 	*protp = ucs.ucs_prot;
560a61ed2ceSHans Rosenfeld 	*iccp = card;
561a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
562118b2dbfSJoshua M. Clulow }
563118b2dbfSJoshua M. Clulow 
564118b2dbfSJoshua M. Clulow /*
565118b2dbfSJoshua M. Clulow  * The Windows documentation suggests that all of the input/output arguments
566118b2dbfSJoshua M. Clulow  * (other than the handle) are effectively optional.
567118b2dbfSJoshua M. Clulow  */
568118b2dbfSJoshua M. Clulow LONG
SCardStatus(SCARDHANDLE arg,LPSTR readerp,LPDWORD readerlenp,LPDWORD statep,LPDWORD protop,LPBYTE atrp,LPDWORD atrlenp)569118b2dbfSJoshua M. Clulow SCardStatus(SCARDHANDLE arg, LPSTR readerp, LPDWORD readerlenp,
570118b2dbfSJoshua M. Clulow     LPDWORD statep, LPDWORD protop, LPBYTE atrp, LPDWORD atrlenp)
571118b2dbfSJoshua M. Clulow {
572118b2dbfSJoshua M. Clulow 	pcsc_card_t *card = arg;
573118b2dbfSJoshua M. Clulow 	pcsc_hdl_t *hdl = card->pcc_hdl;
574118b2dbfSJoshua M. Clulow 	LONG ret = SCARD_S_SUCCESS;
575118b2dbfSJoshua M. Clulow 
576118b2dbfSJoshua M. Clulow 	if (statep == NULL && protop == NULL && atrlenp == NULL) {
577118b2dbfSJoshua M. Clulow 		/*
578118b2dbfSJoshua M. Clulow 		 * There is no need to perform the status ioctl.
579118b2dbfSJoshua M. Clulow 		 */
580118b2dbfSJoshua M. Clulow 		goto name;
581118b2dbfSJoshua M. Clulow 	}
582118b2dbfSJoshua M. Clulow 
583118b2dbfSJoshua M. Clulow 	uccid_cmd_status_t ucs = { .ucs_version = UCCID_CURRENT_VERSION };
584118b2dbfSJoshua M. Clulow 	if (ioctl(card->pcc_fd, UCCID_CMD_STATUS, &ucs) != 0) {
585118b2dbfSJoshua M. Clulow 		VERIFY3S(errno, ==, ENODEV);
586118b2dbfSJoshua M. Clulow 		ret = SCARD_E_READER_UNAVAILABLE;
587118b2dbfSJoshua M. Clulow 		goto out;
588118b2dbfSJoshua M. Clulow 	}
589118b2dbfSJoshua M. Clulow 
590118b2dbfSJoshua M. Clulow 	if (statep != NULL) {
591118b2dbfSJoshua M. Clulow 		if (!(ucs.ucs_status & UCCID_STATUS_F_CARD_PRESENT)) {
592118b2dbfSJoshua M. Clulow 			*statep = SCARD_ABSENT;
593118b2dbfSJoshua M. Clulow 		} else if (ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE) {
594118b2dbfSJoshua M. Clulow 			if (ucs.ucs_status & UCCID_STATUS_F_PARAMS_VALID) {
595118b2dbfSJoshua M. Clulow 				*statep = SCARD_SPECIFIC;
596118b2dbfSJoshua M. Clulow 			} else {
597118b2dbfSJoshua M. Clulow 				*statep = SCARD_POWERED;
598118b2dbfSJoshua M. Clulow 			}
599118b2dbfSJoshua M. Clulow 		} else {
600118b2dbfSJoshua M. Clulow 			*statep = SCARD_PRESENT;
601118b2dbfSJoshua M. Clulow 		}
602118b2dbfSJoshua M. Clulow 	}
603118b2dbfSJoshua M. Clulow 
604118b2dbfSJoshua M. Clulow 	if (protop != NULL) {
605118b2dbfSJoshua M. Clulow 		if (ucs.ucs_status & UCCID_STATUS_F_PARAMS_VALID) {
606118b2dbfSJoshua M. Clulow 			switch (ucs.ucs_prot) {
607118b2dbfSJoshua M. Clulow 			case UCCID_PROT_T0:
608118b2dbfSJoshua M. Clulow 				*protop = SCARD_PROTOCOL_T0;
609118b2dbfSJoshua M. Clulow 				break;
610118b2dbfSJoshua M. Clulow 			case UCCID_PROT_T1:
611118b2dbfSJoshua M. Clulow 				*protop = SCARD_PROTOCOL_T1;
612118b2dbfSJoshua M. Clulow 				break;
613118b2dbfSJoshua M. Clulow 			default:
614118b2dbfSJoshua M. Clulow 				*protop = SCARD_PROTOCOL_UNDEFINED;
615118b2dbfSJoshua M. Clulow 				break;
616118b2dbfSJoshua M. Clulow 			}
617118b2dbfSJoshua M. Clulow 		} else {
618118b2dbfSJoshua M. Clulow 			/*
619118b2dbfSJoshua M. Clulow 			 * If SCARD_SPECIFIC is not returned as the card
620118b2dbfSJoshua M. Clulow 			 * state, this value is not considered meaningful.
621118b2dbfSJoshua M. Clulow 			 */
622118b2dbfSJoshua M. Clulow 			*protop = SCARD_PROTOCOL_UNDEFINED;
623118b2dbfSJoshua M. Clulow 		}
624118b2dbfSJoshua M. Clulow 	}
625118b2dbfSJoshua M. Clulow 
626118b2dbfSJoshua M. Clulow 	if (atrlenp != NULL) {
627118b2dbfSJoshua M. Clulow 		uint8_t *ubuf;
628118b2dbfSJoshua M. Clulow 		uint32_t len = *atrlenp;
629118b2dbfSJoshua M. Clulow 		if (len != SCARD_AUTOALLOCATE) {
630118b2dbfSJoshua M. Clulow 			if (len < ucs.ucs_atrlen) {
631118b2dbfSJoshua M. Clulow 				*atrlenp = ucs.ucs_atrlen;
632118b2dbfSJoshua M. Clulow 				ret = SCARD_E_INSUFFICIENT_BUFFER;
633118b2dbfSJoshua M. Clulow 				goto out;
634118b2dbfSJoshua M. Clulow 			}
635118b2dbfSJoshua M. Clulow 
636118b2dbfSJoshua M. Clulow 			if (atrp == NULL) {
637118b2dbfSJoshua M. Clulow 				ret = SCARD_E_INVALID_PARAMETER;
638118b2dbfSJoshua M. Clulow 				goto out;
639118b2dbfSJoshua M. Clulow 			}
640118b2dbfSJoshua M. Clulow 
641118b2dbfSJoshua M. Clulow 			ubuf = atrp;
642118b2dbfSJoshua M. Clulow 		} else {
643118b2dbfSJoshua M. Clulow 			if ((ubuf = pcsc_mem_alloc(hdl, ucs.ucs_atrlen)) ==
644118b2dbfSJoshua M. Clulow 			    NULL) {
645118b2dbfSJoshua M. Clulow 				ret = SCARD_E_NO_MEMORY;
646118b2dbfSJoshua M. Clulow 				goto out;
647118b2dbfSJoshua M. Clulow 			}
648118b2dbfSJoshua M. Clulow 
649118b2dbfSJoshua M. Clulow 			*((LPBYTE *)atrp) = ubuf;
650118b2dbfSJoshua M. Clulow 		}
651118b2dbfSJoshua M. Clulow 
652118b2dbfSJoshua M. Clulow 		bcopy(ucs.ucs_atr, ubuf, ucs.ucs_atrlen);
653118b2dbfSJoshua M. Clulow 		*atrlenp = ucs.ucs_atrlen;
654118b2dbfSJoshua M. Clulow 	}
655118b2dbfSJoshua M. Clulow 
656118b2dbfSJoshua M. Clulow name:
657118b2dbfSJoshua M. Clulow 	if (readerlenp != NULL) {
658118b2dbfSJoshua M. Clulow 		char *ubuf;
659118b2dbfSJoshua M. Clulow 		uint32_t rlen = *readerlenp;
660118b2dbfSJoshua M. Clulow 		if (rlen != SCARD_AUTOALLOCATE) {
661118b2dbfSJoshua M. Clulow 			if (rlen < card->pcc_namelen) {
662118b2dbfSJoshua M. Clulow 				*readerlenp = card->pcc_namelen;
663118b2dbfSJoshua M. Clulow 				ret = SCARD_E_INSUFFICIENT_BUFFER;
664118b2dbfSJoshua M. Clulow 				goto out;
665118b2dbfSJoshua M. Clulow 			}
666118b2dbfSJoshua M. Clulow 
667118b2dbfSJoshua M. Clulow 			if (readerp == NULL) {
668118b2dbfSJoshua M. Clulow 				ret = SCARD_E_INVALID_PARAMETER;
669118b2dbfSJoshua M. Clulow 				goto out;
670118b2dbfSJoshua M. Clulow 			}
671118b2dbfSJoshua M. Clulow 
672118b2dbfSJoshua M. Clulow 			ubuf = readerp;
673118b2dbfSJoshua M. Clulow 		} else {
674118b2dbfSJoshua M. Clulow 			if ((ubuf = pcsc_mem_alloc(hdl, card->pcc_namelen)) ==
675118b2dbfSJoshua M. Clulow 			    NULL) {
676118b2dbfSJoshua M. Clulow 				ret = SCARD_E_NO_MEMORY;
677118b2dbfSJoshua M. Clulow 				goto out;
678118b2dbfSJoshua M. Clulow 			}
679118b2dbfSJoshua M. Clulow 
680118b2dbfSJoshua M. Clulow 			*((LPSTR *)readerp) = ubuf;
681118b2dbfSJoshua M. Clulow 		}
682118b2dbfSJoshua M. Clulow 
683118b2dbfSJoshua M. Clulow 		/*
684118b2dbfSJoshua M. Clulow 		 * We stored the reader name as a multi-string in
685118b2dbfSJoshua M. Clulow 		 * pcsc_card_alloc(), so we can just copy out the whole value
686118b2dbfSJoshua M. Clulow 		 * here without further modification:
687118b2dbfSJoshua M. Clulow 		 */
688118b2dbfSJoshua M. Clulow 		bcopy(card->pcc_name, ubuf, card->pcc_namelen);
689118b2dbfSJoshua M. Clulow 	}
690118b2dbfSJoshua M. Clulow 
691118b2dbfSJoshua M. Clulow out:
692a61ed2ceSHans Rosenfeld 	return (ret);
693a61ed2ceSHans Rosenfeld }
694a61ed2ceSHans Rosenfeld 
695a61ed2ceSHans Rosenfeld LONG
SCardDisconnect(SCARDHANDLE arg,DWORD disposition)696a61ed2ceSHans Rosenfeld SCardDisconnect(SCARDHANDLE arg, DWORD disposition)
697a61ed2ceSHans Rosenfeld {
698a61ed2ceSHans Rosenfeld 	pcsc_card_t *card = arg;
699a61ed2ceSHans Rosenfeld 
700a61ed2ceSHans Rosenfeld 	if (arg == NULL) {
701a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_HANDLE);
702a61ed2ceSHans Rosenfeld 	}
703a61ed2ceSHans Rosenfeld 
704118b2dbfSJoshua M. Clulow 	switch (disposition) {
705118b2dbfSJoshua M. Clulow 	case SCARD_RESET_CARD: {
706118b2dbfSJoshua M. Clulow 		/*
707118b2dbfSJoshua M. Clulow 		 * To reset the card, we first need to get exclusive access to
708118b2dbfSJoshua M. Clulow 		 * the card.
709118b2dbfSJoshua M. Clulow 		 */
710118b2dbfSJoshua M. Clulow 		uccid_cmd_txn_begin_t txnbegin = {
711118b2dbfSJoshua M. Clulow 			.uct_version = UCCID_CURRENT_VERSION,
712118b2dbfSJoshua M. Clulow 		};
713118b2dbfSJoshua M. Clulow 		if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txnbegin) != 0) {
714118b2dbfSJoshua M. Clulow 			VERIFY3S(errno, !=, EFAULT);
715118b2dbfSJoshua M. Clulow 
716118b2dbfSJoshua M. Clulow 			switch (errno) {
717118b2dbfSJoshua M. Clulow 			case ENODEV:
718118b2dbfSJoshua M. Clulow 				/*
719118b2dbfSJoshua M. Clulow 				 * If the card is no longer present, we cannot
720118b2dbfSJoshua M. Clulow 				 * reset it.
721118b2dbfSJoshua M. Clulow 				 */
722118b2dbfSJoshua M. Clulow 				goto close;
723118b2dbfSJoshua M. Clulow 			case EEXIST:
724118b2dbfSJoshua M. Clulow 				break;
725118b2dbfSJoshua M. Clulow 			case EBUSY:
726118b2dbfSJoshua M. Clulow 				return (SCARD_E_SHARING_VIOLATION);
727118b2dbfSJoshua M. Clulow 			default:
728118b2dbfSJoshua M. Clulow 				return (SCARD_F_UNKNOWN_ERROR);
729118b2dbfSJoshua M. Clulow 			}
730118b2dbfSJoshua M. Clulow 		}
731118b2dbfSJoshua M. Clulow 
732118b2dbfSJoshua M. Clulow 		/*
733118b2dbfSJoshua M. Clulow 		 * Once we have begun the transaction, we can end it
734118b2dbfSJoshua M. Clulow 		 * immediately while requesting a reset before the next
735118b2dbfSJoshua M. Clulow 		 * transaction.
736118b2dbfSJoshua M. Clulow 		 */
737118b2dbfSJoshua M. Clulow 		uccid_cmd_txn_end_t txnend = {
738118b2dbfSJoshua M. Clulow 			.uct_version = UCCID_CURRENT_VERSION,
739118b2dbfSJoshua M. Clulow 			.uct_flags = UCCID_TXN_END_RESET,
740118b2dbfSJoshua M. Clulow 		};
741118b2dbfSJoshua M. Clulow 		if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txnend) != 0) {
742118b2dbfSJoshua M. Clulow 			VERIFY3S(errno, !=, EFAULT);
743118b2dbfSJoshua M. Clulow 
744118b2dbfSJoshua M. Clulow 			switch (errno) {
745118b2dbfSJoshua M. Clulow 			case ENODEV:
746118b2dbfSJoshua M. Clulow 				goto close;
747118b2dbfSJoshua M. Clulow 			default:
748118b2dbfSJoshua M. Clulow 				return (SCARD_F_UNKNOWN_ERROR);
749118b2dbfSJoshua M. Clulow 			}
750118b2dbfSJoshua M. Clulow 		}
751118b2dbfSJoshua M. Clulow 	}
752118b2dbfSJoshua M. Clulow 	case SCARD_LEAVE_CARD:
753118b2dbfSJoshua M. Clulow 		break;
754118b2dbfSJoshua M. Clulow 	default:
755a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
756a61ed2ceSHans Rosenfeld 	}
757a61ed2ceSHans Rosenfeld 
758118b2dbfSJoshua M. Clulow close:
759a61ed2ceSHans Rosenfeld 	if (close(card->pcc_fd) != 0) {
760a61ed2ceSHans Rosenfeld 		return (SCARD_F_UNKNOWN_ERROR);
761a61ed2ceSHans Rosenfeld 	}
762118b2dbfSJoshua M. Clulow 	card->pcc_fd = -1;
763a61ed2ceSHans Rosenfeld 
764118b2dbfSJoshua M. Clulow 	pcsc_card_free(card);
765a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
766a61ed2ceSHans Rosenfeld }
767a61ed2ceSHans Rosenfeld 
768a61ed2ceSHans Rosenfeld LONG
SCardBeginTransaction(SCARDHANDLE arg)769a61ed2ceSHans Rosenfeld SCardBeginTransaction(SCARDHANDLE arg)
770a61ed2ceSHans Rosenfeld {
771a61ed2ceSHans Rosenfeld 	uccid_cmd_txn_begin_t txn;
772a61ed2ceSHans Rosenfeld 	pcsc_card_t *card = arg;
773a61ed2ceSHans Rosenfeld 
774a61ed2ceSHans Rosenfeld 	if (card == NULL) {
775a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_HANDLE);
776a61ed2ceSHans Rosenfeld 	}
777a61ed2ceSHans Rosenfeld 
778a61ed2ceSHans Rosenfeld 	/*
779*cd934150SJoshua M. Clulow 	 * According to the Windows documentation, this function "waits for the
780*cd934150SJoshua M. Clulow 	 * completion of all other transactions before it begins".  Software in
781*cd934150SJoshua M. Clulow 	 * the wild has been observed to be confused by the
782*cd934150SJoshua M. Clulow 	 * SCARD_E_SHARING_VIOLATION condition we would previously return if
783*cd934150SJoshua M. Clulow 	 * the card was already in use.  As a compatibility measure, we omit
784*cd934150SJoshua M. Clulow 	 * the UCCID_TXN_DONT_BLOCK flag so that the kernel will sleep until
785*cd934150SJoshua M. Clulow 	 * the card is no longer busy before returning.
786a61ed2ceSHans Rosenfeld 	 */
787a61ed2ceSHans Rosenfeld 	bzero(&txn, sizeof (uccid_cmd_txn_begin_t));
788a61ed2ceSHans Rosenfeld 	txn.uct_version = UCCID_CURRENT_VERSION;
789a61ed2ceSHans Rosenfeld 
790a61ed2ceSHans Rosenfeld 	if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txn) != 0) {
791a61ed2ceSHans Rosenfeld 		VERIFY3S(errno, !=, EFAULT);
792a61ed2ceSHans Rosenfeld 		switch (errno) {
793a61ed2ceSHans Rosenfeld 		case ENODEV:
794a61ed2ceSHans Rosenfeld 			return (SCARD_E_READER_UNAVAILABLE);
795a61ed2ceSHans Rosenfeld 		case EEXIST:
796a61ed2ceSHans Rosenfeld 			/*
797a61ed2ceSHans Rosenfeld 			 * This is an odd case. It's used to tell us that we
798a61ed2ceSHans Rosenfeld 			 * already have it. For now, just treat it as success.
799a61ed2ceSHans Rosenfeld 			 */
800a61ed2ceSHans Rosenfeld 			return (SCARD_S_SUCCESS);
801a61ed2ceSHans Rosenfeld 		case EBUSY:
802a61ed2ceSHans Rosenfeld 			return (SCARD_E_SHARING_VIOLATION);
803a61ed2ceSHans Rosenfeld 		/*
804a61ed2ceSHans Rosenfeld 		 * EINPROGRESS is a weird case. It means that we were trying to
805a61ed2ceSHans Rosenfeld 		 * grab a hold while another instance using the same handle was.
806a61ed2ceSHans Rosenfeld 		 * For now, treat it as an unknown error.
807a61ed2ceSHans Rosenfeld 		 */
808a61ed2ceSHans Rosenfeld 		case EINPROGRESS:
809a61ed2ceSHans Rosenfeld 		case EINTR:
810a61ed2ceSHans Rosenfeld 		default:
811a61ed2ceSHans Rosenfeld 			return (SCARD_F_UNKNOWN_ERROR);
812a61ed2ceSHans Rosenfeld 		}
813a61ed2ceSHans Rosenfeld 	}
814a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
815a61ed2ceSHans Rosenfeld }
816a61ed2ceSHans Rosenfeld 
817a61ed2ceSHans Rosenfeld LONG
SCardEndTransaction(SCARDHANDLE arg,DWORD state)818a61ed2ceSHans Rosenfeld SCardEndTransaction(SCARDHANDLE arg, DWORD state)
819a61ed2ceSHans Rosenfeld {
820a61ed2ceSHans Rosenfeld 	uccid_cmd_txn_end_t txn;
821a61ed2ceSHans Rosenfeld 	pcsc_card_t *card = arg;
822a61ed2ceSHans Rosenfeld 
823a61ed2ceSHans Rosenfeld 	if (card == NULL) {
824a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_HANDLE);
825a61ed2ceSHans Rosenfeld 	}
826a61ed2ceSHans Rosenfeld 
827a61ed2ceSHans Rosenfeld 	bzero(&txn, sizeof (uccid_cmd_txn_end_t));
828a61ed2ceSHans Rosenfeld 	txn.uct_version = UCCID_CURRENT_VERSION;
829a61ed2ceSHans Rosenfeld 
830a61ed2ceSHans Rosenfeld 	switch (state) {
831a61ed2ceSHans Rosenfeld 	case SCARD_LEAVE_CARD:
832a61ed2ceSHans Rosenfeld 		txn.uct_flags = UCCID_TXN_END_RELEASE;
833a61ed2ceSHans Rosenfeld 		break;
834a61ed2ceSHans Rosenfeld 	case SCARD_RESET_CARD:
835a61ed2ceSHans Rosenfeld 		txn.uct_flags = UCCID_TXN_END_RESET;
836a61ed2ceSHans Rosenfeld 		break;
837a61ed2ceSHans Rosenfeld 	case SCARD_UNPOWER_CARD:
838a61ed2ceSHans Rosenfeld 	case SCARD_EJECT_CARD:
839a61ed2ceSHans Rosenfeld 	default:
840a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
841a61ed2ceSHans Rosenfeld 	}
842a61ed2ceSHans Rosenfeld 
843a61ed2ceSHans Rosenfeld 	if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txn) != 0) {
844a61ed2ceSHans Rosenfeld 		VERIFY3S(errno, !=, EFAULT);
845a61ed2ceSHans Rosenfeld 		switch (errno) {
846a61ed2ceSHans Rosenfeld 		case ENODEV:
847a61ed2ceSHans Rosenfeld 			return (SCARD_E_READER_UNAVAILABLE);
848a61ed2ceSHans Rosenfeld 		case ENXIO:
849a61ed2ceSHans Rosenfeld 			return (SCARD_E_SHARING_VIOLATION);
850a61ed2ceSHans Rosenfeld 		default:
851a61ed2ceSHans Rosenfeld 			return (SCARD_F_UNKNOWN_ERROR);
852a61ed2ceSHans Rosenfeld 		}
853a61ed2ceSHans Rosenfeld 	}
854a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
855a61ed2ceSHans Rosenfeld }
856a61ed2ceSHans Rosenfeld 
857a61ed2ceSHans Rosenfeld LONG
SCardReconnect(SCARDHANDLE arg,DWORD mode,DWORD prots,DWORD init,LPDWORD protp)858a61ed2ceSHans Rosenfeld SCardReconnect(SCARDHANDLE arg, DWORD mode, DWORD prots, DWORD init,
859a61ed2ceSHans Rosenfeld     LPDWORD protp)
860a61ed2ceSHans Rosenfeld {
861a61ed2ceSHans Rosenfeld 	uccid_cmd_status_t ucs;
862a61ed2ceSHans Rosenfeld 	pcsc_card_t *card = arg;
863a61ed2ceSHans Rosenfeld 	LONG ret;
864a61ed2ceSHans Rosenfeld 
865a61ed2ceSHans Rosenfeld 	if (card == NULL) {
866a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_HANDLE);
867a61ed2ceSHans Rosenfeld 	}
868a61ed2ceSHans Rosenfeld 
869a61ed2ceSHans Rosenfeld 	if (protp == NULL) {
870a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_PARAMETER);
871a61ed2ceSHans Rosenfeld 	}
872a61ed2ceSHans Rosenfeld 
873a61ed2ceSHans Rosenfeld 	if (mode != SCARD_SHARE_SHARED) {
874a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
875a61ed2ceSHans Rosenfeld 	}
876a61ed2ceSHans Rosenfeld 
877a61ed2ceSHans Rosenfeld 	if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
878a61ed2ceSHans Rosenfeld 	    SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
879a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
880a61ed2ceSHans Rosenfeld 	}
881a61ed2ceSHans Rosenfeld 
882a61ed2ceSHans Rosenfeld 	if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
883a61ed2ceSHans Rosenfeld 		return (SCARD_E_UNSUPPORTED_FEATURE);
884a61ed2ceSHans Rosenfeld 	}
885a61ed2ceSHans Rosenfeld 
886a61ed2ceSHans Rosenfeld 	if (init != SCARD_LEAVE_CARD) {
887a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_VALUE);
888a61ed2ceSHans Rosenfeld 	}
889a61ed2ceSHans Rosenfeld 
890a61ed2ceSHans Rosenfeld 	if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
891a61ed2ceSHans Rosenfeld 		return (ret);
892a61ed2ceSHans Rosenfeld 
893a61ed2ceSHans Rosenfeld 	*protp = ucs.ucs_prot;
894a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
895a61ed2ceSHans Rosenfeld }
896a61ed2ceSHans Rosenfeld 
897a61ed2ceSHans Rosenfeld LONG
SCardTransmit(SCARDHANDLE arg,const SCARD_IO_REQUEST * sendreq,LPCBYTE sendbuf,DWORD sendlen,SCARD_IO_REQUEST * recvreq,LPBYTE recvbuf,LPDWORD recvlenp)898a61ed2ceSHans Rosenfeld SCardTransmit(SCARDHANDLE arg, const SCARD_IO_REQUEST *sendreq,
899a61ed2ceSHans Rosenfeld     LPCBYTE sendbuf, DWORD sendlen, SCARD_IO_REQUEST *recvreq, LPBYTE recvbuf,
900a61ed2ceSHans Rosenfeld     LPDWORD recvlenp)
901a61ed2ceSHans Rosenfeld {
902a61ed2ceSHans Rosenfeld 	int len;
903a61ed2ceSHans Rosenfeld 	ssize_t ret;
904a61ed2ceSHans Rosenfeld 	pcsc_card_t *card = arg;
905a61ed2ceSHans Rosenfeld 
906a61ed2ceSHans Rosenfeld 	if (card == NULL) {
907a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_HANDLE);
908a61ed2ceSHans Rosenfeld 	}
909a61ed2ceSHans Rosenfeld 
910a61ed2ceSHans Rosenfeld 	/*
911a61ed2ceSHans Rosenfeld 	 * Ignore sendreq / recvreq.
912a61ed2ceSHans Rosenfeld 	 */
913a61ed2ceSHans Rosenfeld 	if (sendbuf == NULL || recvbuf == NULL || recvlenp == NULL) {
914a61ed2ceSHans Rosenfeld 		return (SCARD_E_INVALID_PARAMETER);
915a61ed2ceSHans Rosenfeld 	}
916a61ed2ceSHans Rosenfeld 
917a61ed2ceSHans Rosenfeld 	/*
918a61ed2ceSHans Rosenfeld 	 * The CCID write will always consume all data or none.
919a61ed2ceSHans Rosenfeld 	 */
920a61ed2ceSHans Rosenfeld 	ret = write(card->pcc_fd, sendbuf, sendlen);
921a61ed2ceSHans Rosenfeld 	if (ret == -1) {
922a61ed2ceSHans Rosenfeld 		switch (errno) {
923a61ed2ceSHans Rosenfeld 		case E2BIG:
924a61ed2ceSHans Rosenfeld 			return (SCARD_E_INVALID_PARAMETER);
925a61ed2ceSHans Rosenfeld 		case ENODEV:
926a61ed2ceSHans Rosenfeld 			return (SCARD_E_READER_UNAVAILABLE);
927a61ed2ceSHans Rosenfeld 		case EACCES:
928a61ed2ceSHans Rosenfeld 		case EBUSY:
929a61ed2ceSHans Rosenfeld 			return (SCARD_E_SHARING_VIOLATION);
930a61ed2ceSHans Rosenfeld 		case ENXIO:
931a61ed2ceSHans Rosenfeld 			return (SCARD_W_REMOVED_CARD);
932a61ed2ceSHans Rosenfeld 		case EFAULT:
933a61ed2ceSHans Rosenfeld 			return (SCARD_E_INVALID_PARAMETER);
934a61ed2ceSHans Rosenfeld 		case ENOMEM:
935a61ed2ceSHans Rosenfeld 		default:
936a61ed2ceSHans Rosenfeld 			return (SCARD_F_UNKNOWN_ERROR);
937a61ed2ceSHans Rosenfeld 		}
938a61ed2ceSHans Rosenfeld 	}
939a61ed2ceSHans Rosenfeld 	ASSERT3S(ret, ==, sendlen);
940a61ed2ceSHans Rosenfeld 
941a61ed2ceSHans Rosenfeld 	/*
942a61ed2ceSHans Rosenfeld 	 * Now, we should be able to block in read.
943a61ed2ceSHans Rosenfeld 	 */
944a61ed2ceSHans Rosenfeld 	ret = read(card->pcc_fd, recvbuf, *recvlenp);
945a61ed2ceSHans Rosenfeld 	if (ret == -1) {
946a61ed2ceSHans Rosenfeld 		switch (errno) {
947a61ed2ceSHans Rosenfeld 		case EINVAL:
948a61ed2ceSHans Rosenfeld 		case EOVERFLOW:
949a61ed2ceSHans Rosenfeld 			/*
950a61ed2ceSHans Rosenfeld 			 * This means that we need to update len with the real
951a61ed2ceSHans Rosenfeld 			 * one.
952a61ed2ceSHans Rosenfeld 			 */
953a61ed2ceSHans Rosenfeld 			if (ioctl(card->pcc_fd, FIONREAD, &len) != 0) {
954a61ed2ceSHans Rosenfeld 				return (SCARD_F_UNKNOWN_ERROR);
955a61ed2ceSHans Rosenfeld 			}
956a61ed2ceSHans Rosenfeld 			*recvlenp = len;
957a61ed2ceSHans Rosenfeld 			return (SCARD_E_INSUFFICIENT_BUFFER);
958a61ed2ceSHans Rosenfeld 		case ENODEV:
959a61ed2ceSHans Rosenfeld 			return (SCARD_E_READER_UNAVAILABLE);
960a61ed2ceSHans Rosenfeld 		case EACCES:
961a61ed2ceSHans Rosenfeld 		case EBUSY:
962a61ed2ceSHans Rosenfeld 			return (SCARD_E_SHARING_VIOLATION);
963a61ed2ceSHans Rosenfeld 		case EFAULT:
964a61ed2ceSHans Rosenfeld 			return (SCARD_E_INVALID_PARAMETER);
965a61ed2ceSHans Rosenfeld 		case ENODATA:
966a61ed2ceSHans Rosenfeld 		default:
967a61ed2ceSHans Rosenfeld 			return (SCARD_F_UNKNOWN_ERROR);
968a61ed2ceSHans Rosenfeld 		}
969a61ed2ceSHans Rosenfeld 	}
970a61ed2ceSHans Rosenfeld 
971a61ed2ceSHans Rosenfeld 	*recvlenp = ret;
972a61ed2ceSHans Rosenfeld 
973a61ed2ceSHans Rosenfeld 	return (SCARD_S_SUCCESS);
974a61ed2ceSHans Rosenfeld }
975