xref: /illumos-gate/usr/src/cmd/ccidadm/ccidadm.c (revision a61ed2ce)
1*a61ed2ceSHans Rosenfeld /*
2*a61ed2ceSHans Rosenfeld  * This file and its contents are supplied under the terms of the
3*a61ed2ceSHans Rosenfeld  * Common Development and Distribution License ("CDDL"), version 1.0.
4*a61ed2ceSHans Rosenfeld  * You may only use this file in accordance with the terms of version
5*a61ed2ceSHans Rosenfeld  * 1.0 of the CDDL.
6*a61ed2ceSHans Rosenfeld  *
7*a61ed2ceSHans Rosenfeld  * A full copy of the text of the CDDL should have accompanied this
8*a61ed2ceSHans Rosenfeld  * source.  A copy of the CDDL is also available via the Internet at
9*a61ed2ceSHans Rosenfeld  * http://www.illumos.org/license/CDDL.
10*a61ed2ceSHans Rosenfeld  */
11*a61ed2ceSHans Rosenfeld 
12*a61ed2ceSHans Rosenfeld /*
13*a61ed2ceSHans Rosenfeld  * Copyright 2019, Joyent, Inc.
14*a61ed2ceSHans Rosenfeld  */
15*a61ed2ceSHans Rosenfeld 
16*a61ed2ceSHans Rosenfeld /*
17*a61ed2ceSHans Rosenfeld  * Print out information about a CCID device.
18*a61ed2ceSHans Rosenfeld  */
19*a61ed2ceSHans Rosenfeld 
20*a61ed2ceSHans Rosenfeld #include <sys/types.h>
21*a61ed2ceSHans Rosenfeld #include <sys/stat.h>
22*a61ed2ceSHans Rosenfeld #include <fcntl.h>
23*a61ed2ceSHans Rosenfeld #include <err.h>
24*a61ed2ceSHans Rosenfeld #include <stdlib.h>
25*a61ed2ceSHans Rosenfeld #include <strings.h>
26*a61ed2ceSHans Rosenfeld #include <unistd.h>
27*a61ed2ceSHans Rosenfeld #include <ofmt.h>
28*a61ed2ceSHans Rosenfeld #include <libgen.h>
29*a61ed2ceSHans Rosenfeld #include <ctype.h>
30*a61ed2ceSHans Rosenfeld #include <errno.h>
31*a61ed2ceSHans Rosenfeld #include <limits.h>
32*a61ed2ceSHans Rosenfeld #include <libcmdutils.h>
33*a61ed2ceSHans Rosenfeld #include <fts.h>
34*a61ed2ceSHans Rosenfeld 
35*a61ed2ceSHans Rosenfeld #include <sys/usb/clients/ccid/uccid.h>
36*a61ed2ceSHans Rosenfeld #include <atr.h>
37*a61ed2ceSHans Rosenfeld 
38*a61ed2ceSHans Rosenfeld #define	EXIT_USAGE	2
39*a61ed2ceSHans Rosenfeld 
40*a61ed2ceSHans Rosenfeld static const char *ccidadm_pname;
41*a61ed2ceSHans Rosenfeld 
42*a61ed2ceSHans Rosenfeld #define	CCID_ROOT	"/dev/ccid/"
43*a61ed2ceSHans Rosenfeld 
44*a61ed2ceSHans Rosenfeld typedef enum {
45*a61ed2ceSHans Rosenfeld 	CCIDADM_LIST_DEVICE,
46*a61ed2ceSHans Rosenfeld 	CCIDADM_LIST_PRODUCT,
47*a61ed2ceSHans Rosenfeld 	CCIDADM_LIST_STATE,
48*a61ed2ceSHans Rosenfeld 	CCIDADM_LIST_TRANSPORT,
49*a61ed2ceSHans Rosenfeld 	CCIDADM_LIST_SUPPORTED,
50*a61ed2ceSHans Rosenfeld } ccidadm_list_index_t;
51*a61ed2ceSHans Rosenfeld 
52*a61ed2ceSHans Rosenfeld typedef struct ccidadm_pair {
53*a61ed2ceSHans Rosenfeld 	uint32_t	ccp_val;
54*a61ed2ceSHans Rosenfeld 	const char	*ccp_name;
55*a61ed2ceSHans Rosenfeld } ccidadm_pair_t;
56*a61ed2ceSHans Rosenfeld 
57*a61ed2ceSHans Rosenfeld typedef struct ccid_list_ofmt_arg {
58*a61ed2ceSHans Rosenfeld 	const char		*cloa_name;
59*a61ed2ceSHans Rosenfeld 	uccid_cmd_status_t	*cloa_status;
60*a61ed2ceSHans Rosenfeld } ccid_list_ofmt_arg_t;
61*a61ed2ceSHans Rosenfeld 
62*a61ed2ceSHans Rosenfeld /*
63*a61ed2ceSHans Rosenfeld  * Attempt to open a CCID slot specified by a user. In general, we expect that
64*a61ed2ceSHans Rosenfeld  * users will use a path like "ccid0/slot0". However, they may also specify a
65*a61ed2ceSHans Rosenfeld  * full path. If the card boolean is set to true, that means that they may have
66*a61ed2ceSHans Rosenfeld  * just specified "ccid0", so we need to try to open up the default slot.
67*a61ed2ceSHans Rosenfeld  */
68*a61ed2ceSHans Rosenfeld static int
ccidadm_open(const char * base,boolean_t card)69*a61ed2ceSHans Rosenfeld ccidadm_open(const char *base, boolean_t card)
70*a61ed2ceSHans Rosenfeld {
71*a61ed2ceSHans Rosenfeld 	int fd;
72*a61ed2ceSHans Rosenfeld 	char buf[PATH_MAX];
73*a61ed2ceSHans Rosenfeld 
74*a61ed2ceSHans Rosenfeld 	/*
75*a61ed2ceSHans Rosenfeld 	 * If it's an absolute path, just try to open it.
76*a61ed2ceSHans Rosenfeld 	 */
77*a61ed2ceSHans Rosenfeld 	if (base[0] == '/') {
78*a61ed2ceSHans Rosenfeld 		return (open(base, O_RDWR));
79*a61ed2ceSHans Rosenfeld 	}
80*a61ed2ceSHans Rosenfeld 
81*a61ed2ceSHans Rosenfeld 	/*
82*a61ed2ceSHans Rosenfeld 	 * For a card, try to append slot0 first.
83*a61ed2ceSHans Rosenfeld 	 */
84*a61ed2ceSHans Rosenfeld 	if (card) {
85*a61ed2ceSHans Rosenfeld 		if (snprintf(buf, sizeof (buf), "%s/%s/slot0", CCID_ROOT,
86*a61ed2ceSHans Rosenfeld 		    base) >= sizeof (buf)) {
87*a61ed2ceSHans Rosenfeld 			errno = ENAMETOOLONG;
88*a61ed2ceSHans Rosenfeld 			return (-1);
89*a61ed2ceSHans Rosenfeld 		}
90*a61ed2ceSHans Rosenfeld 
91*a61ed2ceSHans Rosenfeld 		if ((fd = open(buf, O_RDWR)) >= 0) {
92*a61ed2ceSHans Rosenfeld 			return (fd);
93*a61ed2ceSHans Rosenfeld 		}
94*a61ed2ceSHans Rosenfeld 
95*a61ed2ceSHans Rosenfeld 		if (errno != ENOENT && errno != ENOTDIR) {
96*a61ed2ceSHans Rosenfeld 			return (fd);
97*a61ed2ceSHans Rosenfeld 		}
98*a61ed2ceSHans Rosenfeld 	}
99*a61ed2ceSHans Rosenfeld 
100*a61ed2ceSHans Rosenfeld 	if (snprintf(buf, sizeof (buf), "%s/%s", CCID_ROOT, base) >=
101*a61ed2ceSHans Rosenfeld 	    sizeof (buf)) {
102*a61ed2ceSHans Rosenfeld 		errno = ENAMETOOLONG;
103*a61ed2ceSHans Rosenfeld 		return (-1);
104*a61ed2ceSHans Rosenfeld 	}
105*a61ed2ceSHans Rosenfeld 
106*a61ed2ceSHans Rosenfeld 	return (open(buf, O_RDWR));
107*a61ed2ceSHans Rosenfeld }
108*a61ed2ceSHans Rosenfeld 
109*a61ed2ceSHans Rosenfeld static void
ccidadm_iter(boolean_t readeronly,boolean_t newline,void (* cb)(int,const char *,void *),void * arg)110*a61ed2ceSHans Rosenfeld ccidadm_iter(boolean_t readeronly, boolean_t newline,
111*a61ed2ceSHans Rosenfeld     void(*cb)(int, const char *, void *), void *arg)
112*a61ed2ceSHans Rosenfeld {
113*a61ed2ceSHans Rosenfeld 	FTS *fts;
114*a61ed2ceSHans Rosenfeld 	FTSENT *ent;
115*a61ed2ceSHans Rosenfeld 	char *const paths[] = { CCID_ROOT, NULL };
116*a61ed2ceSHans Rosenfeld 	int fd;
117*a61ed2ceSHans Rosenfeld 	boolean_t first = B_TRUE;
118*a61ed2ceSHans Rosenfeld 
119*a61ed2ceSHans Rosenfeld 	fts = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR, NULL);
120*a61ed2ceSHans Rosenfeld 	if (fts == NULL) {
121*a61ed2ceSHans Rosenfeld 		err(EXIT_FAILURE, "failed to create directory stream");
122*a61ed2ceSHans Rosenfeld 	}
123*a61ed2ceSHans Rosenfeld 
124*a61ed2ceSHans Rosenfeld 	while ((ent = fts_read(fts)) != NULL) {
125*a61ed2ceSHans Rosenfeld 		const char *name;
126*a61ed2ceSHans Rosenfeld 
127*a61ed2ceSHans Rosenfeld 		/* Skip the root and post-order dirs */
128*a61ed2ceSHans Rosenfeld 		if (ent->fts_level == 0 || ent->fts_info == FTS_DP) {
129*a61ed2ceSHans Rosenfeld 			continue;
130*a61ed2ceSHans Rosenfeld 		}
131*a61ed2ceSHans Rosenfeld 		if (readeronly && ent->fts_level != 1) {
132*a61ed2ceSHans Rosenfeld 			continue;
133*a61ed2ceSHans Rosenfeld 		} else if (!readeronly && ent->fts_level != 2) {
134*a61ed2ceSHans Rosenfeld 			continue;
135*a61ed2ceSHans Rosenfeld 		}
136*a61ed2ceSHans Rosenfeld 
137*a61ed2ceSHans Rosenfeld 		if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS) {
138*a61ed2ceSHans Rosenfeld 			warn("skipping %s, failed to get information: %s",
139*a61ed2ceSHans Rosenfeld 			    ent->fts_name, strerror(ent->fts_errno));
140*a61ed2ceSHans Rosenfeld 			continue;
141*a61ed2ceSHans Rosenfeld 		}
142*a61ed2ceSHans Rosenfeld 
143*a61ed2ceSHans Rosenfeld 		name = ent->fts_path + strlen(CCID_ROOT);
144*a61ed2ceSHans Rosenfeld 		if ((fd = ccidadm_open(name, readeronly)) < 0) {
145*a61ed2ceSHans Rosenfeld 			err(EXIT_FAILURE, "failed to open %s", name);
146*a61ed2ceSHans Rosenfeld 		}
147*a61ed2ceSHans Rosenfeld 
148*a61ed2ceSHans Rosenfeld 		if (!first && newline) {
149*a61ed2ceSHans Rosenfeld 			(void) printf("\n");
150*a61ed2ceSHans Rosenfeld 		}
151*a61ed2ceSHans Rosenfeld 		first = B_FALSE;
152*a61ed2ceSHans Rosenfeld 		cb(fd, name, arg);
153*a61ed2ceSHans Rosenfeld 		(void) close(fd);
154*a61ed2ceSHans Rosenfeld 	}
155*a61ed2ceSHans Rosenfeld 
156*a61ed2ceSHans Rosenfeld 	(void) fts_close(fts);
157*a61ed2ceSHans Rosenfeld }
158*a61ed2ceSHans Rosenfeld 
159*a61ed2ceSHans Rosenfeld static void
ccidadm_list_slot_status_str(uccid_cmd_status_t * ucs,char * buf,uint_t buflen)160*a61ed2ceSHans Rosenfeld ccidadm_list_slot_status_str(uccid_cmd_status_t *ucs, char *buf, uint_t buflen)
161*a61ed2ceSHans Rosenfeld {
162*a61ed2ceSHans Rosenfeld 	if (!(ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT)) {
163*a61ed2ceSHans Rosenfeld 		(void) snprintf(buf, buflen, "missing");
164*a61ed2ceSHans Rosenfeld 		return;
165*a61ed2ceSHans Rosenfeld 	}
166*a61ed2ceSHans Rosenfeld 
167*a61ed2ceSHans Rosenfeld 	if (ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) {
168*a61ed2ceSHans Rosenfeld 		(void) snprintf(buf, buflen, "activated");
169*a61ed2ceSHans Rosenfeld 		return;
170*a61ed2ceSHans Rosenfeld 	}
171*a61ed2ceSHans Rosenfeld 
172*a61ed2ceSHans Rosenfeld 	(void) snprintf(buf, buflen, "unactivated");
173*a61ed2ceSHans Rosenfeld }
174*a61ed2ceSHans Rosenfeld 
175*a61ed2ceSHans Rosenfeld static boolean_t
ccidadm_list_slot_transport_str(uccid_cmd_status_t * ucs,char * buf,uint_t buflen)176*a61ed2ceSHans Rosenfeld ccidadm_list_slot_transport_str(uccid_cmd_status_t *ucs, char *buf,
177*a61ed2ceSHans Rosenfeld     uint_t buflen)
178*a61ed2ceSHans Rosenfeld {
179*a61ed2ceSHans Rosenfeld 	const char *prot;
180*a61ed2ceSHans Rosenfeld 	const char *tran;
181*a61ed2ceSHans Rosenfeld 	uint_t bits = CCID_CLASS_F_TPDU_XCHG | CCID_CLASS_F_SHORT_APDU_XCHG |
182*a61ed2ceSHans Rosenfeld 	    CCID_CLASS_F_EXT_APDU_XCHG;
183*a61ed2ceSHans Rosenfeld 
184*a61ed2ceSHans Rosenfeld 	switch (ucs->ucs_class.ccd_dwFeatures & bits) {
185*a61ed2ceSHans Rosenfeld 	case 0:
186*a61ed2ceSHans Rosenfeld 		tran = "character";
187*a61ed2ceSHans Rosenfeld 		break;
188*a61ed2ceSHans Rosenfeld 	case CCID_CLASS_F_TPDU_XCHG:
189*a61ed2ceSHans Rosenfeld 		tran = "TPDU";
190*a61ed2ceSHans Rosenfeld 		break;
191*a61ed2ceSHans Rosenfeld 	case CCID_CLASS_F_SHORT_APDU_XCHG:
192*a61ed2ceSHans Rosenfeld 	case CCID_CLASS_F_EXT_APDU_XCHG:
193*a61ed2ceSHans Rosenfeld 		tran = "APDU";
194*a61ed2ceSHans Rosenfeld 		break;
195*a61ed2ceSHans Rosenfeld 	default:
196*a61ed2ceSHans Rosenfeld 		tran = "unknown";
197*a61ed2ceSHans Rosenfeld 		break;
198*a61ed2ceSHans Rosenfeld 	}
199*a61ed2ceSHans Rosenfeld 
200*a61ed2ceSHans Rosenfeld 	if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) != 0) {
201*a61ed2ceSHans Rosenfeld 		switch (ucs->ucs_prot) {
202*a61ed2ceSHans Rosenfeld 		case UCCID_PROT_T0:
203*a61ed2ceSHans Rosenfeld 			prot = " (T=0)";
204*a61ed2ceSHans Rosenfeld 			break;
205*a61ed2ceSHans Rosenfeld 		case UCCID_PROT_T1:
206*a61ed2ceSHans Rosenfeld 			prot = " (T=1)";
207*a61ed2ceSHans Rosenfeld 			break;
208*a61ed2ceSHans Rosenfeld 		default:
209*a61ed2ceSHans Rosenfeld 			prot = "";
210*a61ed2ceSHans Rosenfeld 			break;
211*a61ed2ceSHans Rosenfeld 		}
212*a61ed2ceSHans Rosenfeld 	} else {
213*a61ed2ceSHans Rosenfeld 		prot = "";
214*a61ed2ceSHans Rosenfeld 	}
215*a61ed2ceSHans Rosenfeld 
216*a61ed2ceSHans Rosenfeld 	return (snprintf(buf, buflen, "%s%s", tran, prot) < buflen);
217*a61ed2ceSHans Rosenfeld }
218*a61ed2ceSHans Rosenfeld 
219*a61ed2ceSHans Rosenfeld static boolean_t
ccidadm_list_slot_usable_str(uccid_cmd_status_t * ucs,char * buf,uint_t buflen)220*a61ed2ceSHans Rosenfeld ccidadm_list_slot_usable_str(uccid_cmd_status_t *ucs, char *buf,
221*a61ed2ceSHans Rosenfeld     uint_t buflen)
222*a61ed2ceSHans Rosenfeld {
223*a61ed2ceSHans Rosenfeld 	const char *un = "";
224*a61ed2ceSHans Rosenfeld 	ccid_class_features_t feat;
225*a61ed2ceSHans Rosenfeld 	uint_t prot = CCID_CLASS_F_SHORT_APDU_XCHG | CCID_CLASS_F_EXT_APDU_XCHG;
226*a61ed2ceSHans Rosenfeld 	uint_t param = CCID_CLASS_F_AUTO_PARAM_NEG | CCID_CLASS_F_AUTO_PPS;
227*a61ed2ceSHans Rosenfeld 	uint_t clock = CCID_CLASS_F_AUTO_BAUD | CCID_CLASS_F_AUTO_ICC_CLOCK;
228*a61ed2ceSHans Rosenfeld 
229*a61ed2ceSHans Rosenfeld 	feat = ucs->ucs_class.ccd_dwFeatures;
230*a61ed2ceSHans Rosenfeld 
231*a61ed2ceSHans Rosenfeld 	if ((feat & prot) == 0 ||
232*a61ed2ceSHans Rosenfeld 	    (feat & param) != param ||
233*a61ed2ceSHans Rosenfeld 	    (feat & clock) != clock) {
234*a61ed2ceSHans Rosenfeld 		un = "un";
235*a61ed2ceSHans Rosenfeld 	}
236*a61ed2ceSHans Rosenfeld 
237*a61ed2ceSHans Rosenfeld 	return (snprintf(buf, buflen, "%ssupported", un) < buflen);
238*a61ed2ceSHans Rosenfeld }
239*a61ed2ceSHans Rosenfeld 
240*a61ed2ceSHans Rosenfeld static boolean_t
ccidadm_list_ofmt_cb(ofmt_arg_t * ofmt,char * buf,uint_t buflen)241*a61ed2ceSHans Rosenfeld ccidadm_list_ofmt_cb(ofmt_arg_t *ofmt, char *buf, uint_t buflen)
242*a61ed2ceSHans Rosenfeld {
243*a61ed2ceSHans Rosenfeld 	ccid_list_ofmt_arg_t *cloa = ofmt->ofmt_cbarg;
244*a61ed2ceSHans Rosenfeld 
245*a61ed2ceSHans Rosenfeld 	switch (ofmt->ofmt_id) {
246*a61ed2ceSHans Rosenfeld 	case CCIDADM_LIST_DEVICE:
247*a61ed2ceSHans Rosenfeld 		if (snprintf(buf, buflen, "%s", cloa->cloa_name) >= buflen) {
248*a61ed2ceSHans Rosenfeld 			return (B_FALSE);
249*a61ed2ceSHans Rosenfeld 		}
250*a61ed2ceSHans Rosenfeld 		break;
251*a61ed2ceSHans Rosenfeld 	case CCIDADM_LIST_PRODUCT:
252*a61ed2ceSHans Rosenfeld 		if (snprintf(buf, buflen, "%s",
253*a61ed2ceSHans Rosenfeld 		    cloa->cloa_status->ucs_product) >= buflen) {
254*a61ed2ceSHans Rosenfeld 			return (B_FALSE);
255*a61ed2ceSHans Rosenfeld 		}
256*a61ed2ceSHans Rosenfeld 		break;
257*a61ed2ceSHans Rosenfeld 	case CCIDADM_LIST_STATE:
258*a61ed2ceSHans Rosenfeld 		ccidadm_list_slot_status_str(cloa->cloa_status, buf, buflen);
259*a61ed2ceSHans Rosenfeld 		break;
260*a61ed2ceSHans Rosenfeld 	case CCIDADM_LIST_TRANSPORT:
261*a61ed2ceSHans Rosenfeld 		return (ccidadm_list_slot_transport_str(cloa->cloa_status, buf,
262*a61ed2ceSHans Rosenfeld 		    buflen));
263*a61ed2ceSHans Rosenfeld 		break;
264*a61ed2ceSHans Rosenfeld 	case CCIDADM_LIST_SUPPORTED:
265*a61ed2ceSHans Rosenfeld 		return (ccidadm_list_slot_usable_str(cloa->cloa_status, buf,
266*a61ed2ceSHans Rosenfeld 		    buflen));
267*a61ed2ceSHans Rosenfeld 		break;
268*a61ed2ceSHans Rosenfeld 	default:
269*a61ed2ceSHans Rosenfeld 		return (B_FALSE);
270*a61ed2ceSHans Rosenfeld 	}
271*a61ed2ceSHans Rosenfeld 
272*a61ed2ceSHans Rosenfeld 	return (B_TRUE);
273*a61ed2ceSHans Rosenfeld }
274*a61ed2ceSHans Rosenfeld 
275*a61ed2ceSHans Rosenfeld static void
ccidadm_list_slot(int slotfd,const char * name,void * arg)276*a61ed2ceSHans Rosenfeld ccidadm_list_slot(int slotfd, const char *name, void *arg)
277*a61ed2ceSHans Rosenfeld {
278*a61ed2ceSHans Rosenfeld 	uccid_cmd_status_t ucs;
279*a61ed2ceSHans Rosenfeld 	ofmt_handle_t ofmt = arg;
280*a61ed2ceSHans Rosenfeld 	ccid_list_ofmt_arg_t cloa;
281*a61ed2ceSHans Rosenfeld 
282*a61ed2ceSHans Rosenfeld 	bzero(&ucs, sizeof (ucs));
283*a61ed2ceSHans Rosenfeld 	ucs.ucs_version = UCCID_CURRENT_VERSION;
284*a61ed2ceSHans Rosenfeld 
285*a61ed2ceSHans Rosenfeld 	if (ioctl(slotfd, UCCID_CMD_STATUS, &ucs) != 0) {
286*a61ed2ceSHans Rosenfeld 		err(EXIT_FAILURE, "failed to issue status ioctl to %s", name);
287*a61ed2ceSHans Rosenfeld 	}
288*a61ed2ceSHans Rosenfeld 
289*a61ed2ceSHans Rosenfeld 	if ((ucs.ucs_status & UCCID_STATUS_F_PRODUCT_VALID) == 0) {
290*a61ed2ceSHans Rosenfeld 		(void) strlcpy(ucs.ucs_product, "<unknown>",
291*a61ed2ceSHans Rosenfeld 		    sizeof (ucs.ucs_product));
292*a61ed2ceSHans Rosenfeld 	}
293*a61ed2ceSHans Rosenfeld 
294*a61ed2ceSHans Rosenfeld 	cloa.cloa_name = name;
295*a61ed2ceSHans Rosenfeld 	cloa.cloa_status = &ucs;
296*a61ed2ceSHans Rosenfeld 	ofmt_print(ofmt, &cloa);
297*a61ed2ceSHans Rosenfeld }
298*a61ed2ceSHans Rosenfeld 
299*a61ed2ceSHans Rosenfeld static ofmt_field_t ccidadm_list_fields[] = {
300*a61ed2ceSHans Rosenfeld 	{ "PRODUCT",	24,	CCIDADM_LIST_PRODUCT,	ccidadm_list_ofmt_cb },
301*a61ed2ceSHans Rosenfeld 	{ "DEVICE",	16,	CCIDADM_LIST_DEVICE,	ccidadm_list_ofmt_cb },
302*a61ed2ceSHans Rosenfeld 	{ "CARD STATE",	12,	CCIDADM_LIST_STATE,	ccidadm_list_ofmt_cb },
303*a61ed2ceSHans Rosenfeld 	{ "TRANSPORT",	12,	CCIDADM_LIST_TRANSPORT,	ccidadm_list_ofmt_cb },
304*a61ed2ceSHans Rosenfeld 	{ "SUPPORTED",	12,	CCIDADM_LIST_SUPPORTED,	ccidadm_list_ofmt_cb },
305*a61ed2ceSHans Rosenfeld 	{ NULL,		0,	0,			NULL	}
306*a61ed2ceSHans Rosenfeld };
307*a61ed2ceSHans Rosenfeld 
308*a61ed2ceSHans Rosenfeld static void
ccidadm_do_list(int argc,char * argv[])309*a61ed2ceSHans Rosenfeld ccidadm_do_list(int argc, char *argv[])
310*a61ed2ceSHans Rosenfeld {
311*a61ed2ceSHans Rosenfeld 	ofmt_handle_t ofmt;
312*a61ed2ceSHans Rosenfeld 
313*a61ed2ceSHans Rosenfeld 	if (argc != 0) {
314*a61ed2ceSHans Rosenfeld 		errx(EXIT_USAGE, "list command does not take arguments\n");
315*a61ed2ceSHans Rosenfeld 	}
316*a61ed2ceSHans Rosenfeld 
317*a61ed2ceSHans Rosenfeld 	if (ofmt_open(NULL, ccidadm_list_fields, 0, 0, &ofmt) != OFMT_SUCCESS) {
318*a61ed2ceSHans Rosenfeld 		errx(EXIT_FAILURE, "failed to initialize ofmt state");
319*a61ed2ceSHans Rosenfeld 	}
320*a61ed2ceSHans Rosenfeld 
321*a61ed2ceSHans Rosenfeld 	ccidadm_iter(B_FALSE, B_FALSE, ccidadm_list_slot, ofmt);
322*a61ed2ceSHans Rosenfeld 	ofmt_close(ofmt);
323*a61ed2ceSHans Rosenfeld }
324*a61ed2ceSHans Rosenfeld 
325*a61ed2ceSHans Rosenfeld static void
ccidadm_list_usage(FILE * out)326*a61ed2ceSHans Rosenfeld ccidadm_list_usage(FILE *out)
327*a61ed2ceSHans Rosenfeld {
328*a61ed2ceSHans Rosenfeld 	(void) fprintf(out, "\tlist\n");
329*a61ed2ceSHans Rosenfeld }
330*a61ed2ceSHans Rosenfeld 
331*a61ed2ceSHans Rosenfeld /*
332*a61ed2ceSHans Rosenfeld  * Print out logical information about the ICC's ATR. This includes information
333*a61ed2ceSHans Rosenfeld  * about what protocols it supports, required negotiation, etc.
334*a61ed2ceSHans Rosenfeld  */
335*a61ed2ceSHans Rosenfeld static void
ccidadm_atr_props(uccid_cmd_status_t * ucs)336*a61ed2ceSHans Rosenfeld ccidadm_atr_props(uccid_cmd_status_t *ucs)
337*a61ed2ceSHans Rosenfeld {
338*a61ed2ceSHans Rosenfeld 	int ret;
339*a61ed2ceSHans Rosenfeld 	atr_data_t *data;
340*a61ed2ceSHans Rosenfeld 	atr_protocol_t prots, defprot;
341*a61ed2ceSHans Rosenfeld 	boolean_t negotiate;
342*a61ed2ceSHans Rosenfeld 	atr_data_rate_choice_t rate;
343*a61ed2ceSHans Rosenfeld 	uint32_t bps;
344*a61ed2ceSHans Rosenfeld 
345*a61ed2ceSHans Rosenfeld 	if ((data = atr_data_alloc()) == NULL) {
346*a61ed2ceSHans Rosenfeld 		err(EXIT_FAILURE, "failed to allocate memory for "
347*a61ed2ceSHans Rosenfeld 		    "ATR data");
348*a61ed2ceSHans Rosenfeld 	}
349*a61ed2ceSHans Rosenfeld 
350*a61ed2ceSHans Rosenfeld 	ret = atr_parse(ucs->ucs_atr, ucs->ucs_atrlen, data);
351*a61ed2ceSHans Rosenfeld 	if (ret != ATR_CODE_OK) {
352*a61ed2ceSHans Rosenfeld 		errx(EXIT_FAILURE, "failed to parse ATR data: %s",
353*a61ed2ceSHans Rosenfeld 		    atr_strerror(ret));
354*a61ed2ceSHans Rosenfeld 	}
355*a61ed2ceSHans Rosenfeld 
356*a61ed2ceSHans Rosenfeld 	prots = atr_supported_protocols(data);
357*a61ed2ceSHans Rosenfeld 	(void) printf("ICC supports protocol(s): ");
358*a61ed2ceSHans Rosenfeld 	if (prots == ATR_P_NONE) {
359*a61ed2ceSHans Rosenfeld 		(void) printf("none\n");
360*a61ed2ceSHans Rosenfeld 		atr_data_free(data);
361*a61ed2ceSHans Rosenfeld 		return;
362*a61ed2ceSHans Rosenfeld 	}
363*a61ed2ceSHans Rosenfeld 
364*a61ed2ceSHans Rosenfeld 	(void) printf("%s\n", atr_protocol_to_string(prots));
365*a61ed2ceSHans Rosenfeld 
366*a61ed2ceSHans Rosenfeld 	negotiate = atr_params_negotiable(data);
367*a61ed2ceSHans Rosenfeld 	defprot = atr_default_protocol(data);
368*a61ed2ceSHans Rosenfeld 
369*a61ed2ceSHans Rosenfeld 	if (negotiate) {
370*a61ed2ceSHans Rosenfeld 		(void) printf("Card protocol is negotiable; starts with "
371*a61ed2ceSHans Rosenfeld 		    "default %s parameters\n", atr_protocol_to_string(defprot));
372*a61ed2ceSHans Rosenfeld 	} else {
373*a61ed2ceSHans Rosenfeld 		(void) printf("Card protocol is not negotiable; starts with "
374*a61ed2ceSHans Rosenfeld 		    "specific %s parameters\n",
375*a61ed2ceSHans Rosenfeld 		    atr_protocol_to_string(defprot));
376*a61ed2ceSHans Rosenfeld 	}
377*a61ed2ceSHans Rosenfeld 
378*a61ed2ceSHans Rosenfeld 	/*
379*a61ed2ceSHans Rosenfeld 	 * For each supported protocol, figure out parameters we would
380*a61ed2ceSHans Rosenfeld 	 * negotiate. We only need to warn about auto-negotiation if this
381*a61ed2ceSHans Rosenfeld 	 * is TPDU or character and specific bits are missing.
382*a61ed2ceSHans Rosenfeld 	 */
383*a61ed2ceSHans Rosenfeld 	if (((ucs->ucs_class.ccd_dwFeatures & (CCID_CLASS_F_SHORT_APDU_XCHG |
384*a61ed2ceSHans Rosenfeld 	    CCID_CLASS_F_EXT_APDU_XCHG)) == 0) &&
385*a61ed2ceSHans Rosenfeld 	    ((ucs->ucs_class.ccd_dwFeatures & (CCID_CLASS_F_AUTO_PARAM_NEG |
386*a61ed2ceSHans Rosenfeld 	    CCID_CLASS_F_AUTO_PPS)) == 0)) {
387*a61ed2ceSHans Rosenfeld 		(void) printf("CCID/ICC require explicit TPDU parameter/PPS "
388*a61ed2ceSHans Rosenfeld 		    "negotiation\n");
389*a61ed2ceSHans Rosenfeld 	}
390*a61ed2ceSHans Rosenfeld 
391*a61ed2ceSHans Rosenfeld 	/*
392*a61ed2ceSHans Rosenfeld 	 * Determine which set of Di/Fi values we should use and how we should
393*a61ed2ceSHans Rosenfeld 	 * get there (note a reader may not have to set them).
394*a61ed2ceSHans Rosenfeld 	 */
395*a61ed2ceSHans Rosenfeld 	rate = atr_data_rate(data, &ucs->ucs_class, NULL, 0, &bps);
396*a61ed2ceSHans Rosenfeld 	switch (rate) {
397*a61ed2ceSHans Rosenfeld 	case ATR_RATE_USEDEFAULT:
398*a61ed2ceSHans Rosenfeld 		(void) printf("Reader will run ICC at the default (Di=1/Fi=1) "
399*a61ed2ceSHans Rosenfeld 		    "speed\n");
400*a61ed2ceSHans Rosenfeld 		break;
401*a61ed2ceSHans Rosenfeld 	case ATR_RATE_USEATR:
402*a61ed2ceSHans Rosenfeld 		(void) printf("Reader will run ICC at ICC's Di/Fi values\n");
403*a61ed2ceSHans Rosenfeld 		break;
404*a61ed2ceSHans Rosenfeld 	case ATR_RATE_USEATR_SETRATE:
405*a61ed2ceSHans Rosenfeld 		(void) printf("Reader will run ICC at ICC's Di/Fi values, but "
406*a61ed2ceSHans Rosenfeld 		    "must set data rate to %u bps\n", bps);
407*a61ed2ceSHans Rosenfeld 		break;
408*a61ed2ceSHans Rosenfeld 	case ATR_RATE_UNSUPPORTED:
409*a61ed2ceSHans Rosenfeld 		(void) printf("Reader cannot run ICC due to Di/Fi mismatch\n");
410*a61ed2ceSHans Rosenfeld 		break;
411*a61ed2ceSHans Rosenfeld 	default:
412*a61ed2ceSHans Rosenfeld 		(void) printf("Cannot determine Di/Fi rate, unexpected "
413*a61ed2ceSHans Rosenfeld 		    "value: %u\n", rate);
414*a61ed2ceSHans Rosenfeld 		break;
415*a61ed2ceSHans Rosenfeld 	}
416*a61ed2ceSHans Rosenfeld 	if (prots & ATR_P_T0) {
417*a61ed2ceSHans Rosenfeld 		uint8_t fi, di;
418*a61ed2ceSHans Rosenfeld 		atr_convention_t conv;
419*a61ed2ceSHans Rosenfeld 		atr_clock_stop_t clock;
420*a61ed2ceSHans Rosenfeld 
421*a61ed2ceSHans Rosenfeld 		fi = atr_fi_index(data);
422*a61ed2ceSHans Rosenfeld 		di = atr_di_index(data);
423*a61ed2ceSHans Rosenfeld 		conv = atr_convention(data);
424*a61ed2ceSHans Rosenfeld 		clock = atr_clock_stop(data);
425*a61ed2ceSHans Rosenfeld 		(void) printf("T=0 properties that would be negotiated:\n");
426*a61ed2ceSHans Rosenfeld 		(void) printf("  + Fi/Fmax Index: %u (Fi %s/Fmax %s MHz)\n",
427*a61ed2ceSHans Rosenfeld 		    fi, atr_fi_index_to_string(fi),
428*a61ed2ceSHans Rosenfeld 		    atr_fmax_index_to_string(fi));
429*a61ed2ceSHans Rosenfeld 		(void) printf("  + Di Index: %u (Di %s)\n", di,
430*a61ed2ceSHans Rosenfeld 		    atr_di_index_to_string(di));
431*a61ed2ceSHans Rosenfeld 		(void) printf("  + Clock Convention: %u (%s)\n", conv,
432*a61ed2ceSHans Rosenfeld 		    atr_convention_to_string(conv));
433*a61ed2ceSHans Rosenfeld 		(void) printf("  + Extra Guardtime: %u\n",
434*a61ed2ceSHans Rosenfeld 		    atr_extra_guardtime(data));
435*a61ed2ceSHans Rosenfeld 		(void) printf("  + WI: %u\n", atr_t0_wi(data));
436*a61ed2ceSHans Rosenfeld 		(void) printf("  + Clock Stop: %u (%s)\n", clock,
437*a61ed2ceSHans Rosenfeld 		    atr_clock_stop_to_string(clock));
438*a61ed2ceSHans Rosenfeld 	}
439*a61ed2ceSHans Rosenfeld 
440*a61ed2ceSHans Rosenfeld 	if (prots & ATR_P_T1) {
441*a61ed2ceSHans Rosenfeld 		uint8_t fi, di;
442*a61ed2ceSHans Rosenfeld 		atr_clock_stop_t clock;
443*a61ed2ceSHans Rosenfeld 		atr_t1_checksum_t cksum;
444*a61ed2ceSHans Rosenfeld 
445*a61ed2ceSHans Rosenfeld 		fi = atr_fi_index(data);
446*a61ed2ceSHans Rosenfeld 		di = atr_di_index(data);
447*a61ed2ceSHans Rosenfeld 		clock = atr_clock_stop(data);
448*a61ed2ceSHans Rosenfeld 		cksum = atr_t1_checksum(data);
449*a61ed2ceSHans Rosenfeld 		(void) printf("T=1 properties that would be negotiated:\n");
450*a61ed2ceSHans Rosenfeld 		(void) printf("  + Fi/Fmax Index: %u (Fi %s/Fmax %s MHz)\n",
451*a61ed2ceSHans Rosenfeld 		    fi, atr_fi_index_to_string(fi),
452*a61ed2ceSHans Rosenfeld 		    atr_fmax_index_to_string(fi));
453*a61ed2ceSHans Rosenfeld 		(void) printf("  + Di Index: %u (Di %s)\n", di,
454*a61ed2ceSHans Rosenfeld 		    atr_di_index_to_string(di));
455*a61ed2ceSHans Rosenfeld 		(void) printf("  + Checksum: %s\n",
456*a61ed2ceSHans Rosenfeld 		    cksum == ATR_T1_CHECKSUM_CRC ? "CRC" : "LRC");
457*a61ed2ceSHans Rosenfeld 		(void) printf("  + Extra Guardtime: %u\n",
458*a61ed2ceSHans Rosenfeld 		    atr_extra_guardtime(data));
459*a61ed2ceSHans Rosenfeld 		(void) printf("  + BWI: %u\n", atr_t1_bwi(data));
460*a61ed2ceSHans Rosenfeld 		(void) printf("  + CWI: %u\n", atr_t1_cwi(data));
461*a61ed2ceSHans Rosenfeld 		(void) printf("  + Clock Stop: %u (%s)\n", clock,
462*a61ed2ceSHans Rosenfeld 		    atr_clock_stop_to_string(clock));
463*a61ed2ceSHans Rosenfeld 		(void) printf("  + IFSC: %u\n", atr_t1_ifsc(data));
464*a61ed2ceSHans Rosenfeld 		(void) printf("  + CCID Supports NAD: %s\n",
465*a61ed2ceSHans Rosenfeld 		    ucs->ucs_class.ccd_dwFeatures & CCID_CLASS_F_ALTNAD_SUP ?
466*a61ed2ceSHans Rosenfeld 		    "yes" : "no");
467*a61ed2ceSHans Rosenfeld 	}
468*a61ed2ceSHans Rosenfeld 
469*a61ed2ceSHans Rosenfeld 	atr_data_free(data);
470*a61ed2ceSHans Rosenfeld }
471*a61ed2ceSHans Rosenfeld 
472*a61ed2ceSHans Rosenfeld static void
ccidadm_atr_verbose(uccid_cmd_status_t * ucs)473*a61ed2ceSHans Rosenfeld ccidadm_atr_verbose(uccid_cmd_status_t *ucs)
474*a61ed2ceSHans Rosenfeld {
475*a61ed2ceSHans Rosenfeld 	int ret;
476*a61ed2ceSHans Rosenfeld 	atr_data_t *data;
477*a61ed2ceSHans Rosenfeld 
478*a61ed2ceSHans Rosenfeld 	if ((data = atr_data_alloc()) == NULL) {
479*a61ed2ceSHans Rosenfeld 		err(EXIT_FAILURE, "failed to allocate memory for "
480*a61ed2ceSHans Rosenfeld 		    "ATR data");
481*a61ed2ceSHans Rosenfeld 	}
482*a61ed2ceSHans Rosenfeld 
483*a61ed2ceSHans Rosenfeld 	ret = atr_parse(ucs->ucs_atr, ucs->ucs_atrlen, data);
484*a61ed2ceSHans Rosenfeld 	if (ret != ATR_CODE_OK) {
485*a61ed2ceSHans Rosenfeld 		errx(EXIT_FAILURE, "failed to parse ATR data: %s",
486*a61ed2ceSHans Rosenfeld 		    atr_strerror(ret));
487*a61ed2ceSHans Rosenfeld 	}
488*a61ed2ceSHans Rosenfeld 	atr_data_dump(data, stdout);
489*a61ed2ceSHans Rosenfeld 	atr_data_free(data);
490*a61ed2ceSHans Rosenfeld }
491*a61ed2ceSHans Rosenfeld 
492*a61ed2ceSHans Rosenfeld typedef struct cciadm_atr_args {
493*a61ed2ceSHans Rosenfeld 	boolean_t caa_hex;
494*a61ed2ceSHans Rosenfeld 	boolean_t caa_props;
495*a61ed2ceSHans Rosenfeld 	boolean_t caa_verbose;
496*a61ed2ceSHans Rosenfeld } ccidadm_atr_args_t;
497*a61ed2ceSHans Rosenfeld 
498*a61ed2ceSHans Rosenfeld static void
ccidadm_atr_fetch(int fd,const char * name,void * arg)499*a61ed2ceSHans Rosenfeld ccidadm_atr_fetch(int fd, const char *name, void *arg)
500*a61ed2ceSHans Rosenfeld {
501*a61ed2ceSHans Rosenfeld 	uccid_cmd_status_t ucs;
502*a61ed2ceSHans Rosenfeld 	ccidadm_atr_args_t *caa = arg;
503*a61ed2ceSHans Rosenfeld 
504*a61ed2ceSHans Rosenfeld 	bzero(&ucs, sizeof (ucs));
505*a61ed2ceSHans Rosenfeld 	ucs.ucs_version = UCCID_CURRENT_VERSION;
506*a61ed2ceSHans Rosenfeld 
507*a61ed2ceSHans Rosenfeld 	if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) {
508*a61ed2ceSHans Rosenfeld 		err(EXIT_FAILURE, "failed to issue status ioctl to %s",
509*a61ed2ceSHans Rosenfeld 		    name);
510*a61ed2ceSHans Rosenfeld 	}
511*a61ed2ceSHans Rosenfeld 
512*a61ed2ceSHans Rosenfeld 	if (ucs.ucs_atrlen == 0) {
513*a61ed2ceSHans Rosenfeld 		warnx("slot %s has no card inserted or activated", name);
514*a61ed2ceSHans Rosenfeld 		return;
515*a61ed2ceSHans Rosenfeld 	}
516*a61ed2ceSHans Rosenfeld 
517*a61ed2ceSHans Rosenfeld 	(void) printf("ATR for %s (%u bytes):\n", name, ucs.ucs_atrlen);
518*a61ed2ceSHans Rosenfeld 	if (caa->caa_props) {
519*a61ed2ceSHans Rosenfeld 		ccidadm_atr_props(&ucs);
520*a61ed2ceSHans Rosenfeld 	}
521*a61ed2ceSHans Rosenfeld 
522*a61ed2ceSHans Rosenfeld 	if (caa->caa_hex) {
523*a61ed2ceSHans Rosenfeld 		atr_data_hexdump(ucs.ucs_atr, ucs.ucs_atrlen, stdout);
524*a61ed2ceSHans Rosenfeld 	}
525*a61ed2ceSHans Rosenfeld 
526*a61ed2ceSHans Rosenfeld 	if (caa->caa_verbose) {
527*a61ed2ceSHans Rosenfeld 		ccidadm_atr_verbose(&ucs);
528*a61ed2ceSHans Rosenfeld 	}
529*a61ed2ceSHans Rosenfeld }
530*a61ed2ceSHans Rosenfeld 
531*a61ed2ceSHans Rosenfeld static void
ccidadm_do_atr(int argc,char * argv[])532*a61ed2ceSHans Rosenfeld ccidadm_do_atr(int argc, char *argv[])
533*a61ed2ceSHans Rosenfeld {
534*a61ed2ceSHans Rosenfeld 	uint_t i;
535*a61ed2ceSHans Rosenfeld 	int c;
536*a61ed2ceSHans Rosenfeld 	ccidadm_atr_args_t caa;
537*a61ed2ceSHans Rosenfeld 
538*a61ed2ceSHans Rosenfeld 	bzero(&caa, sizeof (caa));
539*a61ed2ceSHans Rosenfeld 	optind = 0;
540*a61ed2ceSHans Rosenfeld 	while ((c = getopt(argc, argv, "vx")) != -1) {
541*a61ed2ceSHans Rosenfeld 		switch (c) {
542*a61ed2ceSHans Rosenfeld 		case 'v':
543*a61ed2ceSHans Rosenfeld 			caa.caa_verbose = B_TRUE;
544*a61ed2ceSHans Rosenfeld 			break;
545*a61ed2ceSHans Rosenfeld 		case 'x':
546*a61ed2ceSHans Rosenfeld 			caa.caa_hex = B_TRUE;
547*a61ed2ceSHans Rosenfeld 			break;
548*a61ed2ceSHans Rosenfeld 		case ':':
549*a61ed2ceSHans Rosenfeld 			errx(EXIT_USAGE, "Option -%c requires an argument\n",
550*a61ed2ceSHans Rosenfeld 			    optopt);
551*a61ed2ceSHans Rosenfeld 			break;
552*a61ed2ceSHans Rosenfeld 		case '?':
553*a61ed2ceSHans Rosenfeld 			errx(EXIT_USAGE, "Unknown option: -%c\n", optopt);
554*a61ed2ceSHans Rosenfeld 			break;
555*a61ed2ceSHans Rosenfeld 		}
556*a61ed2ceSHans Rosenfeld 	}
557*a61ed2ceSHans Rosenfeld 
558*a61ed2ceSHans Rosenfeld 	if (!caa.caa_verbose && !caa.caa_props && !caa.caa_hex) {
559*a61ed2ceSHans Rosenfeld 		caa.caa_props = B_TRUE;
560*a61ed2ceSHans Rosenfeld 	}
561*a61ed2ceSHans Rosenfeld 
562*a61ed2ceSHans Rosenfeld 	argc -= optind;
563*a61ed2ceSHans Rosenfeld 	argv += optind;
564*a61ed2ceSHans Rosenfeld 
565*a61ed2ceSHans Rosenfeld 	if (argc == 0) {
566*a61ed2ceSHans Rosenfeld 		ccidadm_iter(B_FALSE, B_TRUE, ccidadm_atr_fetch, &caa);
567*a61ed2ceSHans Rosenfeld 		return;
568*a61ed2ceSHans Rosenfeld 	}
569*a61ed2ceSHans Rosenfeld 
570*a61ed2ceSHans Rosenfeld 	for (i = 0; i < argc; i++) {
571*a61ed2ceSHans Rosenfeld 		int fd;
572*a61ed2ceSHans Rosenfeld 
573*a61ed2ceSHans Rosenfeld 		if ((fd = ccidadm_open(argv[i], B_FALSE)) < 0) {
574*a61ed2ceSHans Rosenfeld 			warn("failed to open %s", argv[i]);
575*a61ed2ceSHans Rosenfeld 			errx(EXIT_FAILURE, "valid CCID slot?");
576*a61ed2ceSHans Rosenfeld 		}
577*a61ed2ceSHans Rosenfeld 
578*a61ed2ceSHans Rosenfeld 		ccidadm_atr_fetch(fd, argv[i], &caa);
579*a61ed2ceSHans Rosenfeld 		(void) close(fd);
580*a61ed2ceSHans Rosenfeld 		if (i + 1 < argc) {
581*a61ed2ceSHans Rosenfeld 			(void) printf("\n");
582*a61ed2ceSHans Rosenfeld 		}
583*a61ed2ceSHans Rosenfeld 	}
584*a61ed2ceSHans Rosenfeld }
585*a61ed2ceSHans Rosenfeld 
586*a61ed2ceSHans Rosenfeld static void
ccidadm_atr_usage(FILE * out)587*a61ed2ceSHans Rosenfeld ccidadm_atr_usage(FILE *out)
588*a61ed2ceSHans Rosenfeld {
589*a61ed2ceSHans Rosenfeld 	(void) fprintf(out, "\tatr [-vx]\t[device] ...\n");
590*a61ed2ceSHans Rosenfeld }
591*a61ed2ceSHans Rosenfeld 
592*a61ed2ceSHans Rosenfeld static void
ccidadm_print_pairs(uint32_t val,ccidadm_pair_t * ccp)593*a61ed2ceSHans Rosenfeld ccidadm_print_pairs(uint32_t val, ccidadm_pair_t *ccp)
594*a61ed2ceSHans Rosenfeld {
595*a61ed2ceSHans Rosenfeld 	while (ccp->ccp_name != NULL) {
596*a61ed2ceSHans Rosenfeld 		if ((val & ccp->ccp_val) == ccp->ccp_val) {
597*a61ed2ceSHans Rosenfeld 			(void) printf("    + %s\n", ccp->ccp_name);
598*a61ed2ceSHans Rosenfeld 		}
599*a61ed2ceSHans Rosenfeld 		ccp++;
600*a61ed2ceSHans Rosenfeld 	}
601*a61ed2ceSHans Rosenfeld }
602*a61ed2ceSHans Rosenfeld 
603*a61ed2ceSHans Rosenfeld static ccidadm_pair_t ccidadm_p_protocols[] = {
604*a61ed2ceSHans Rosenfeld 	{ 0x01, "T=0" },
605*a61ed2ceSHans Rosenfeld 	{ 0x02, "T=1" },
606*a61ed2ceSHans Rosenfeld 	{ 0x0, NULL }
607*a61ed2ceSHans Rosenfeld };
608*a61ed2ceSHans Rosenfeld 
609*a61ed2ceSHans Rosenfeld static ccidadm_pair_t ccidadm_p_voltages[] = {
610*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_VOLT_5_0, "5.0 V" },
611*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_VOLT_3_0, "3.0 V" },
612*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_VOLT_1_8, "1.8 V" },
613*a61ed2ceSHans Rosenfeld 	{ 0x0, NULL }
614*a61ed2ceSHans Rosenfeld };
615*a61ed2ceSHans Rosenfeld 
616*a61ed2ceSHans Rosenfeld static ccidadm_pair_t ccidadm_p_syncprots[] = {
617*a61ed2ceSHans Rosenfeld 	{ 0x01, "2-Wire Support" },
618*a61ed2ceSHans Rosenfeld 	{ 0x02, "3-Wire Support" },
619*a61ed2ceSHans Rosenfeld 	{ 0x04, "I2C Support" },
620*a61ed2ceSHans Rosenfeld 	{ 0x0, NULL }
621*a61ed2ceSHans Rosenfeld };
622*a61ed2ceSHans Rosenfeld 
623*a61ed2ceSHans Rosenfeld static ccidadm_pair_t ccidadm_p_mechanical[] = {
624*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_MECH_CARD_ACCEPT, "Card Accept Mechanism" },
625*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_MECH_CARD_EJECT, "Card Eject Mechanism" },
626*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_MECH_CARD_CAPTURE, "Card Capture Mechanism" },
627*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_MECH_CARD_LOCK, "Card Lock/Unlock Mechanism" },
628*a61ed2ceSHans Rosenfeld 	{ 0x0, NULL }
629*a61ed2ceSHans Rosenfeld };
630*a61ed2ceSHans Rosenfeld 
631*a61ed2ceSHans Rosenfeld static ccidadm_pair_t ccidadm_p_features[] = {
632*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_PARAM_ATR,
633*a61ed2ceSHans Rosenfeld 	    "Automatic parameter configuration based on ATR data" },
634*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_ICC_ACTIVATE,
635*a61ed2ceSHans Rosenfeld 	    "Automatic activation on ICC insertion" },
636*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_ICC_VOLTAGE, "Automatic ICC voltage selection" },
637*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_ICC_CLOCK,
638*a61ed2ceSHans Rosenfeld 	    "Automatic ICC clock frequency change" },
639*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_BAUD, "Automatic baud rate change" },
640*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_PARAM_NEG,
641*a61ed2ceSHans Rosenfeld 	    "Automatic parameter negotiation by CCID" },
642*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_PPS, "Automatic PPS made by CCID" },
643*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_ICC_CLOCK_STOP, "CCID can set ICC in clock stop mode" },
644*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_ALTNAD_SUP, "NAD value other than zero accepted" },
645*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_AUTO_IFSD, "Automatic IFSD exchange" },
646*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_TPDU_XCHG, "TPDU support" },
647*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_SHORT_APDU_XCHG, "Short APDU support" },
648*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_EXT_APDU_XCHG, "Short and Extended APDU support" },
649*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_F_WAKE_UP, "USB Wake Up signaling support" },
650*a61ed2ceSHans Rosenfeld 	{ 0x0, NULL }
651*a61ed2ceSHans Rosenfeld };
652*a61ed2ceSHans Rosenfeld 
653*a61ed2ceSHans Rosenfeld static ccidadm_pair_t ccidadm_p_pin[] = {
654*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_PIN_VERIFICATION, "PIN verification" },
655*a61ed2ceSHans Rosenfeld 	{ CCID_CLASS_PIN_MODIFICATION, "PIN modification" },
656*a61ed2ceSHans Rosenfeld 	{ 0x0, NULL }
657*a61ed2ceSHans Rosenfeld };
658*a61ed2ceSHans Rosenfeld 
659*a61ed2ceSHans Rosenfeld static void
ccidadm_reader_print(int fd,const char * name,void * unused __unused)660*a61ed2ceSHans Rosenfeld ccidadm_reader_print(int fd, const char *name, void *unused __unused)
661*a61ed2ceSHans Rosenfeld {
662*a61ed2ceSHans Rosenfeld 	uccid_cmd_status_t ucs;
663*a61ed2ceSHans Rosenfeld 	ccid_class_descr_t *cd;
664*a61ed2ceSHans Rosenfeld 	char nnbuf[NN_NUMBUF_SZ + 1];
665*a61ed2ceSHans Rosenfeld 
666*a61ed2ceSHans Rosenfeld 	bzero(&ucs, sizeof (uccid_cmd_status_t));
667*a61ed2ceSHans Rosenfeld 	ucs.ucs_version = UCCID_CURRENT_VERSION;
668*a61ed2ceSHans Rosenfeld 
669*a61ed2ceSHans Rosenfeld 	if (ioctl(fd, UCCID_CMD_STATUS, &ucs) != 0) {
670*a61ed2ceSHans Rosenfeld 		err(EXIT_FAILURE, "failed to issue status ioctl to %s",
671*a61ed2ceSHans Rosenfeld 		    name);
672*a61ed2ceSHans Rosenfeld 	}
673*a61ed2ceSHans Rosenfeld 
674*a61ed2ceSHans Rosenfeld 	cd = &ucs.ucs_class;
675*a61ed2ceSHans Rosenfeld 	(void) printf("Reader %s, CCID class v%u.%u device:\n", name,
676*a61ed2ceSHans Rosenfeld 	    CCID_VERSION_MAJOR(cd->ccd_bcdCCID),
677*a61ed2ceSHans Rosenfeld 	    CCID_VERSION_MINOR(cd->ccd_bcdCCID));
678*a61ed2ceSHans Rosenfeld 
679*a61ed2ceSHans Rosenfeld 	if ((ucs.ucs_status & UCCID_STATUS_F_PRODUCT_VALID) == 0) {
680*a61ed2ceSHans Rosenfeld 		(void) strlcpy(ucs.ucs_product, "<unknown>",
681*a61ed2ceSHans Rosenfeld 		    sizeof (ucs.ucs_product));
682*a61ed2ceSHans Rosenfeld 	}
683*a61ed2ceSHans Rosenfeld 
684*a61ed2ceSHans Rosenfeld 	if ((ucs.ucs_status & UCCID_STATUS_F_SERIAL_VALID) == 0) {
685*a61ed2ceSHans Rosenfeld 		(void) strlcpy(ucs.ucs_serial, "<unknown>",
686*a61ed2ceSHans Rosenfeld 		    sizeof (ucs.ucs_serial));
687*a61ed2ceSHans Rosenfeld 	}
688*a61ed2ceSHans Rosenfeld 
689*a61ed2ceSHans Rosenfeld 	(void) printf("  Product: %s\n", ucs.ucs_product);
690*a61ed2ceSHans Rosenfeld 	(void) printf("  Serial: %s\n", ucs.ucs_serial);
691*a61ed2ceSHans Rosenfeld 	(void) printf("  Slots Present: %u\n", cd->ccd_bMaxSlotIndex + 1);
692*a61ed2ceSHans Rosenfeld 	(void) printf("  Maximum Busy Slots: %u\n", cd->ccd_bMaxCCIDBusySlots);
693*a61ed2ceSHans Rosenfeld 	(void) printf("  Supported Voltages:\n");
694*a61ed2ceSHans Rosenfeld 	ccidadm_print_pairs(cd->ccd_bVoltageSupport, ccidadm_p_voltages);
695*a61ed2ceSHans Rosenfeld 	(void) printf("  Supported Protocols:\n");
696*a61ed2ceSHans Rosenfeld 	ccidadm_print_pairs(cd->ccd_dwProtocols, ccidadm_p_protocols);
697*a61ed2ceSHans Rosenfeld 	nicenum_scale(cd->ccd_dwDefaultClock, 1000, nnbuf,
698*a61ed2ceSHans Rosenfeld 	    sizeof (nnbuf), NN_DIVISOR_1000 | NN_UNIT_SPACE);
699*a61ed2ceSHans Rosenfeld 	(void) printf("  Default Clock: %sHz\n", nnbuf);
700*a61ed2ceSHans Rosenfeld 	nicenum_scale(cd->ccd_dwMaximumClock, 1000, nnbuf,
701*a61ed2ceSHans Rosenfeld 	    sizeof (nnbuf), NN_DIVISOR_1000 | NN_UNIT_SPACE);
702*a61ed2ceSHans Rosenfeld 	(void) printf("  Maximum Clock: %sHz\n", nnbuf);
703*a61ed2ceSHans Rosenfeld 	(void) printf("  Supported Clock Rates: %u\n",
704*a61ed2ceSHans Rosenfeld 	    cd->ccd_bNumClockSupported);
705*a61ed2ceSHans Rosenfeld 	nicenum_scale(cd->ccd_dwDataRate, 1, nnbuf, sizeof (nnbuf),
706*a61ed2ceSHans Rosenfeld 	    NN_DIVISOR_1000 | NN_UNIT_SPACE);
707*a61ed2ceSHans Rosenfeld 	(void) printf("  Default Data Rate: %sbps\n", nnbuf);
708*a61ed2ceSHans Rosenfeld 	nicenum_scale(cd->ccd_dwMaxDataRate, 1, nnbuf, sizeof (nnbuf),
709*a61ed2ceSHans Rosenfeld 	    NN_DIVISOR_1000 | NN_UNIT_SPACE);
710*a61ed2ceSHans Rosenfeld 	(void) printf("  Maximum Data Rate: %sbps\n", nnbuf);
711*a61ed2ceSHans Rosenfeld 	(void) printf("  Supported Data Rates: %u\n",
712*a61ed2ceSHans Rosenfeld 	    cd->ccd_bNumDataRatesSupported);
713*a61ed2ceSHans Rosenfeld 	(void) printf("  Maximum IFSD (T=1 only): %u\n", cd->ccd_dwMaxIFSD);
714*a61ed2ceSHans Rosenfeld 	if (cd->ccd_dwSyncProtocols != 0) {
715*a61ed2ceSHans Rosenfeld 		(void) printf("  Synchronous Protocols Supported:\n");
716*a61ed2ceSHans Rosenfeld 		ccidadm_print_pairs(cd->ccd_dwSyncProtocols,
717*a61ed2ceSHans Rosenfeld 		    ccidadm_p_syncprots);
718*a61ed2ceSHans Rosenfeld 	}
719*a61ed2ceSHans Rosenfeld 	if (cd->ccd_dwMechanical != 0) {
720*a61ed2ceSHans Rosenfeld 		(void) printf("  Mechanical Features:\n");
721*a61ed2ceSHans Rosenfeld 		ccidadm_print_pairs(cd->ccd_dwMechanical, ccidadm_p_mechanical);
722*a61ed2ceSHans Rosenfeld 	}
723*a61ed2ceSHans Rosenfeld 	if (cd->ccd_dwFeatures != 0) {
724*a61ed2ceSHans Rosenfeld 		(void) printf("  Device Features:\n");
725*a61ed2ceSHans Rosenfeld 		ccidadm_print_pairs(cd->ccd_dwFeatures, ccidadm_p_features);
726*a61ed2ceSHans Rosenfeld 	}
727*a61ed2ceSHans Rosenfeld 	(void) printf("  Maximum Message Length: %u bytes\n",
728*a61ed2ceSHans Rosenfeld 	    cd->ccd_dwMaxCCIDMessageLength);
729*a61ed2ceSHans Rosenfeld 	if (cd->ccd_dwFeatures & CCID_CLASS_F_EXT_APDU_XCHG) {
730*a61ed2ceSHans Rosenfeld 		if (cd->ccd_bClassGetResponse == 0xff) {
731*a61ed2ceSHans Rosenfeld 			(void) printf("  Default Get Response Class: echo\n");
732*a61ed2ceSHans Rosenfeld 		} else {
733*a61ed2ceSHans Rosenfeld 			(void) printf("  Default Get Response Class: %u\n",
734*a61ed2ceSHans Rosenfeld 			    cd->ccd_bClassGetResponse);
735*a61ed2ceSHans Rosenfeld 		}
736*a61ed2ceSHans Rosenfeld 		if (cd->ccd_bClassEnvelope == 0xff) {
737*a61ed2ceSHans Rosenfeld 			(void) printf("  Default Envelope Class: echo\n");
738*a61ed2ceSHans Rosenfeld 		} else {
739*a61ed2ceSHans Rosenfeld 			(void) printf("  Default Envelope Class: %u\n",
740*a61ed2ceSHans Rosenfeld 			    cd->ccd_bClassEnvelope);
741*a61ed2ceSHans Rosenfeld 		}
742*a61ed2ceSHans Rosenfeld 	}
743*a61ed2ceSHans Rosenfeld 	if (cd->ccd_wLcdLayout != 0) {
744*a61ed2ceSHans Rosenfeld 		(void) printf("  %2ux%2u LCD present\n",
745*a61ed2ceSHans Rosenfeld 		    cd->ccd_wLcdLayout >> 8, cd->ccd_wLcdLayout & 0xff);
746*a61ed2ceSHans Rosenfeld 	}
747*a61ed2ceSHans Rosenfeld 
748*a61ed2ceSHans Rosenfeld 	if (cd->ccd_bPinSupport) {
749*a61ed2ceSHans Rosenfeld 		(void) printf("  Pin Support:\n");
750*a61ed2ceSHans Rosenfeld 		ccidadm_print_pairs(cd->ccd_bPinSupport, ccidadm_p_pin);
751*a61ed2ceSHans Rosenfeld 	}
752*a61ed2ceSHans Rosenfeld }
753*a61ed2ceSHans Rosenfeld 
754*a61ed2ceSHans Rosenfeld static void
ccidadm_do_reader(int argc,char * argv[])755*a61ed2ceSHans Rosenfeld ccidadm_do_reader(int argc, char *argv[])
756*a61ed2ceSHans Rosenfeld {
757*a61ed2ceSHans Rosenfeld 	int i;
758*a61ed2ceSHans Rosenfeld 
759*a61ed2ceSHans Rosenfeld 	if (argc == 0) {
760*a61ed2ceSHans Rosenfeld 		ccidadm_iter(B_TRUE, B_TRUE, ccidadm_reader_print, NULL);
761*a61ed2ceSHans Rosenfeld 		return;
762*a61ed2ceSHans Rosenfeld 	}
763*a61ed2ceSHans Rosenfeld 
764*a61ed2ceSHans Rosenfeld 	for (i = 0; i < argc; i++) {
765*a61ed2ceSHans Rosenfeld 		int fd;
766*a61ed2ceSHans Rosenfeld 
767*a61ed2ceSHans Rosenfeld 		if ((fd = ccidadm_open(argv[i], B_TRUE)) < 0) {
768*a61ed2ceSHans Rosenfeld 			warn("failed to open %s", argv[i]);
769*a61ed2ceSHans Rosenfeld 			errx(EXIT_FAILURE, "valid ccid reader");
770*a61ed2ceSHans Rosenfeld 		}
771*a61ed2ceSHans Rosenfeld 
772*a61ed2ceSHans Rosenfeld 		ccidadm_reader_print(fd, argv[i], NULL);
773*a61ed2ceSHans Rosenfeld 		(void) close(fd);
774*a61ed2ceSHans Rosenfeld 		if (i + 1 < argc) {
775*a61ed2ceSHans Rosenfeld 			(void) printf("\n");
776*a61ed2ceSHans Rosenfeld 		}
777*a61ed2ceSHans Rosenfeld 	}
778*a61ed2ceSHans Rosenfeld }
779*a61ed2ceSHans Rosenfeld 
780*a61ed2ceSHans Rosenfeld static void
ccidadm_reader_usage(FILE * out)781*a61ed2ceSHans Rosenfeld ccidadm_reader_usage(FILE *out)
782*a61ed2ceSHans Rosenfeld {
783*a61ed2ceSHans Rosenfeld 	(void) fprintf(out, "\treader\t\t[reader] ...\n");
784*a61ed2ceSHans Rosenfeld }
785*a61ed2ceSHans Rosenfeld 
786*a61ed2ceSHans Rosenfeld typedef struct ccidadm_cmdtab {
787*a61ed2ceSHans Rosenfeld 	const char *cc_name;
788*a61ed2ceSHans Rosenfeld 	void (*cc_op)(int, char *[]);
789*a61ed2ceSHans Rosenfeld 	void (*cc_usage)(FILE *);
790*a61ed2ceSHans Rosenfeld } ccidadm_cmdtab_t;
791*a61ed2ceSHans Rosenfeld 
792*a61ed2ceSHans Rosenfeld static ccidadm_cmdtab_t ccidadm_cmds[] = {
793*a61ed2ceSHans Rosenfeld 	{ "list", ccidadm_do_list, ccidadm_list_usage },
794*a61ed2ceSHans Rosenfeld 	{ "atr", ccidadm_do_atr, ccidadm_atr_usage },
795*a61ed2ceSHans Rosenfeld 	{ "reader", ccidadm_do_reader, ccidadm_reader_usage },
796*a61ed2ceSHans Rosenfeld 	{ NULL }
797*a61ed2ceSHans Rosenfeld };
798*a61ed2ceSHans Rosenfeld 
799*a61ed2ceSHans Rosenfeld static int
ccidadm_usage(const char * format,...)800*a61ed2ceSHans Rosenfeld ccidadm_usage(const char *format, ...)
801*a61ed2ceSHans Rosenfeld {
802*a61ed2ceSHans Rosenfeld 	ccidadm_cmdtab_t *tab;
803*a61ed2ceSHans Rosenfeld 
804*a61ed2ceSHans Rosenfeld 	if (format != NULL) {
805*a61ed2ceSHans Rosenfeld 		va_list ap;
806*a61ed2ceSHans Rosenfeld 
807*a61ed2ceSHans Rosenfeld 		va_start(ap, format);
808*a61ed2ceSHans Rosenfeld 		(void) fprintf(stderr, "%s: ", ccidadm_pname);
809*a61ed2ceSHans Rosenfeld 		(void) vfprintf(stderr, format, ap);
810*a61ed2ceSHans Rosenfeld 		(void) fprintf(stderr, "\n");
811*a61ed2ceSHans Rosenfeld 		va_end(ap);
812*a61ed2ceSHans Rosenfeld 	}
813*a61ed2ceSHans Rosenfeld 
814*a61ed2ceSHans Rosenfeld 	(void) fprintf(stderr, "usage:  %s <subcommand> <args> ...\n\n",
815*a61ed2ceSHans Rosenfeld 	    ccidadm_pname);
816*a61ed2ceSHans Rosenfeld 	(void) fprintf(stderr, "Subcommands:\n");
817*a61ed2ceSHans Rosenfeld 	for (tab = ccidadm_cmds; tab->cc_name != NULL; tab++) {
818*a61ed2ceSHans Rosenfeld 		tab->cc_usage(stderr);
819*a61ed2ceSHans Rosenfeld 	}
820*a61ed2ceSHans Rosenfeld 
821*a61ed2ceSHans Rosenfeld 	return (EXIT_USAGE);
822*a61ed2ceSHans Rosenfeld }
823*a61ed2ceSHans Rosenfeld 
824*a61ed2ceSHans Rosenfeld int
main(int argc,char * argv[])825*a61ed2ceSHans Rosenfeld main(int argc, char *argv[])
826*a61ed2ceSHans Rosenfeld {
827*a61ed2ceSHans Rosenfeld 	ccidadm_cmdtab_t *tab;
828*a61ed2ceSHans Rosenfeld 
829*a61ed2ceSHans Rosenfeld 	ccidadm_pname = basename(argv[0]);
830*a61ed2ceSHans Rosenfeld 	if (argc < 2) {
831*a61ed2ceSHans Rosenfeld 		return (ccidadm_usage("missing required subcommand"));
832*a61ed2ceSHans Rosenfeld 	}
833*a61ed2ceSHans Rosenfeld 
834*a61ed2ceSHans Rosenfeld 	for (tab = ccidadm_cmds; tab->cc_name != NULL; tab++) {
835*a61ed2ceSHans Rosenfeld 		if (strcmp(argv[1], tab->cc_name) == 0) {
836*a61ed2ceSHans Rosenfeld 			argc -= 2;
837*a61ed2ceSHans Rosenfeld 			argv += 2;
838*a61ed2ceSHans Rosenfeld 			tab->cc_op(argc, argv);
839*a61ed2ceSHans Rosenfeld 			return (EXIT_SUCCESS);
840*a61ed2ceSHans Rosenfeld 		}
841*a61ed2ceSHans Rosenfeld 	}
842*a61ed2ceSHans Rosenfeld 
843*a61ed2ceSHans Rosenfeld 	return (ccidadm_usage("unknown command: %s", argv[1]));
844*a61ed2ceSHans Rosenfeld }
845