summaryrefslogtreecommitdiff
path: root/usr/src/test/os-tests
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/test/os-tests')
-rw-r--r--usr/src/test/os-tests/runfiles/default.run8
-rw-r--r--usr/src/test/os-tests/tests/Makefile1
-rw-r--r--usr/src/test/os-tests/tests/uccid/Makefile82
-rw-r--r--usr/src/test/os-tests/tests/uccid/atrparse.c731
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-badread.c80
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-basic.c65
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-close.c81
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-loop.c84
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-nonblock.c99
-rw-r--r--usr/src/test/os-tests/tests/uccid/excl-reset.c65
-rw-r--r--usr/src/test/os-tests/tests/uccid/modify.c181
-rw-r--r--usr/src/test/os-tests/tests/uccid/notxn-poll.c57
-rw-r--r--usr/src/test/os-tests/tests/uccid/pollin.c64
-rw-r--r--usr/src/test/os-tests/tests/uccid/pollout.c68
-rw-r--r--usr/src/test/os-tests/tests/uccid/status.c97
-rw-r--r--usr/src/test/os-tests/tests/uccid/txn-pollerr.c88
-rw-r--r--usr/src/test/os-tests/tests/uccid/yk-poll.c108
-rw-r--r--usr/src/test/os-tests/tests/uccid/yk-readonly.c84
-rw-r--r--usr/src/test/os-tests/tests/uccid/yk.c78
19 files changed, 2121 insertions, 0 deletions
diff --git a/usr/src/test/os-tests/runfiles/default.run b/usr/src/test/os-tests/runfiles/default.run
index 9489c2faf6..940b9bf97a 100644
--- a/usr/src/test/os-tests/runfiles/default.run
+++ b/usr/src/test/os-tests/runfiles/default.run
@@ -87,3 +87,11 @@ tests = ['ldt', 'badseg']
[/opt/os-tests/tests/imc_test]
arch = i86pc
+
+#
+# Except atrparse all tests require special hardware (CCID YubiKey) to run,
+# hence they aren't included in the default runfile.
+#
+[/opt/os-tests/tests/uccid]
+arch = i86pc
+tests = ['atrparse']
diff --git a/usr/src/test/os-tests/tests/Makefile b/usr/src/test/os-tests/tests/Makefile
index e3e756c946..3107af540c 100644
--- a/usr/src/test/os-tests/tests/Makefile
+++ b/usr/src/test/os-tests/tests/Makefile
@@ -29,6 +29,7 @@ SUBDIRS = \
stress \
timer \
tmpfs \
+ uccid \
$(SUBDIRS_$(MACH))
PROGS = \
diff --git a/usr/src/test/os-tests/tests/uccid/Makefile b/usr/src/test/os-tests/tests/uccid/Makefile
new file mode 100644
index 0000000000..55fd96bfd5
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/Makefile
@@ -0,0 +1,82 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019, Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/os-tests
+TESTDIR = $(ROOTOPTPKG)/tests/uccid
+
+PROGS = \
+ atrparse \
+ excl-basic \
+ excl-badread \
+ excl-close \
+ excl-loop \
+ excl-nonblock \
+ excl-reset \
+ modify \
+ notxn-poll \
+ status \
+ pollin \
+ pollout \
+ txn-pollerr \
+ yk \
+ yk-poll \
+ yk-readonly
+
+COMMON_OBJS = \
+ atr.o
+
+atrparse := EXTRA_OBJS = $(COMMON_OBJS)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROGS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+CPPFLAGS += -D_REENTRANT -I$(SRC)/common/ccid/
+
+all: $(PROGS)
+
+install: all $(CMDS)
+
+clobber: clean
+ -$(RM) $(PROGS)
+
+clean:
+ -$(RM) *.o
+
+$(PROGS): $(COMMON_OBJS)
+
+$(CMDS): $(TESTDIR) $(PROGS)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+%.o: $(SRC)/common/ccid/%.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+%.o: %.c
+ $(COMPILE.c) -o $@ -c $<
+ $(POST_PROCESS_O)
+
+%: %.o
+ $(LINK.c) -o $@ $< $(EXTRA_OBJS)
+ $(POST_PROCESS)
diff --git a/usr/src/test/os-tests/tests/uccid/atrparse.c b/usr/src/test/os-tests/tests/uccid/atrparse.c
new file mode 100644
index 0000000000..f14bbfd34d
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/atrparse.c
@@ -0,0 +1,731 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can parse various forms of ATR data and detect invalid data.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+
+#include <atr.h>
+
+typedef struct atr_vals {
+} atr_vals_t;
+
+typedef struct atr_test {
+ const char *ar_test;
+ uint8_t ar_len;
+ uint8_t ar_buf[64];
+ atr_parsecode_t ar_retval;
+ /* Everything after this is data from the ATR */
+ atr_protocol_t ar_sup;
+ atr_protocol_t ar_def;
+ boolean_t ar_neg;
+ uint8_t ar_fi;
+ uint8_t ar_di;
+ atr_convention_t ar_conv;
+ uint8_t ar_guard;
+ atr_clock_stop_t ar_stop;
+ /* These will be checked based on sup prot */
+ uint8_t ar_t0_wi;
+ atr_t1_checksum_t ar_t1_cksum;
+ uint8_t ar_t1_bwi;
+ uint8_t ar_t1_cwi;
+ uint8_t ar_t1_ifsc;
+} atr_test_t;
+
+atr_test_t atr_tests[] = {
+ { "zero-length data", 0, { 0 }, ATR_CODE_TOO_SHORT },
+ { "No T0", 1, { 0x3f }, ATR_CODE_TOO_SHORT },
+ { "Too much data", 34, { 0 }, ATR_CODE_TOO_LONG },
+ { "Overrun T0 (1)", 2, { 0x3b, 0x10 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (2)", 2, { 0x3b, 0x80 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (3)", 2, { 0x3b, 0x01 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (4)", 2, { 0x3b, 0x11 }, ATR_CODE_OVERRUN },
+ { "Overrun T0 (5)", 2, { 0x3b, 0xff }, ATR_CODE_OVERRUN },
+ { "Overrun TD1", 3, { 0x3b, 0x80, 0x10 }, ATR_CODE_OVERRUN },
+ { "Overrun TD2", 4, { 0x3b, 0x80, 0x80, 0x10 }, ATR_CODE_OVERRUN },
+ { "Overrun TD", 33, { 0x3b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80 }, ATR_CODE_OVERRUN },
+ { "T0 w/ T=15 and no cksum", 5, { 0x3b, 0x80, 0x80, 0x1f, 0x00 },
+ ATR_CODE_OVERRUN },
+ { "Bad TS (1)", 2, { 0x3a, 0x00 }, ATR_CODE_INVALID_TS },
+ { "Bad TS (2)", 2, { 0xff, 0x00 }, ATR_CODE_INVALID_TS },
+ { "T0 w/ T=15 and bad cksum", 6, { 0x3b, 0x80, 0x80, 0x1f, 0x00, 0x00 },
+ ATR_CODE_CHECKSUM_ERROR },
+ { "T0 w/ T=15 and bad cksum (make sure no TS)", 6,
+ { 0x3b, 0x80, 0x80, 0x1f, 0x00, 0x24 },
+ ATR_CODE_CHECKSUM_ERROR },
+ { "T=15 in TD1", 4, { 0x3b, 0x80, 0x0f, 0x8f }, ATR_CODE_INVALID_TD1 },
+ {
+ .ar_test = "Minimal T0 Direct",
+ .ar_len = 2,
+ .ar_buf = { 0x3b, 0x00 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "Minimal T0 Inverse",
+ .ar_len = 2,
+ .ar_buf = { 0x3f, 0x00 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_INVERSE,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Fi/Di (1)",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x10, 0x24 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 2,
+ .ar_di = 4,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Fi/Di (2)",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x10, 0x93 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 9,
+ .ar_di = 3,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Ignore deprecated TB1",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x20, 0x42 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Ignore deprecated TB2",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x20, 0x42 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Ignore deprecated TB1/TB2",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0xa0, 0x55, 0x20, 0x42 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 Encode TC1",
+ .ar_len = 3,
+ .ar_buf = { 0x3b, 0x40, 0x23 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0x23,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says neg",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x10, 0x00 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says not neg",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x10, 0x80 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says not neg, honor Fi/Di",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x90, 0x24, 0x10, 0x80 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 2,
+ .ar_di = 4,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TA2 says not neg, don't honor Fi/Di",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x90, 0x24, 0x10, 0x90 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 TC2 set",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x40, 0x35 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 0x35,
+ }, {
+ .ar_test = "T0 T15 empty (requires checksum)",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x0f, 0x0f },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (1)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x07, 0x18 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (2)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x47, 0x58 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_LOW,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (3)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x87, 0x98 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_HI,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 T15 Clock Stop (4)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0xc7, 0xd8 },
+ .ar_sup = ATR_P_T0,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_BOTH,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "T0 with random prots",
+ .ar_len = 7,
+ .ar_buf = { 0x3b, 0x80, 0x84, 0x85, 0x88, 0x0f, 0x06 },
+ .ar_sup = ATR_P_T0,
+ /*
+ * This comes from the fact that TD1 is T=4 and that isn't
+ * supported in the system.
+ */
+ .ar_def = ATR_P_NONE,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ }, {
+ .ar_test = "Actual ATR (1, Yubikey4)",
+ .ar_len = 18,
+ .ar_buf = { 0x3b, 0xf8, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe,
+ 0x15, 0x59, 0x75, 0x62, 0x69, 0x6b, 0x65, 0x79, 0x34,
+ 0xd4 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 3,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 1,
+ .ar_t1_cwi = 5,
+ .ar_t1_ifsc = 254
+ }, {
+ .ar_test = "Actual ATR (2)",
+ .ar_len = 19,
+ .ar_buf = { 0x3b, 0xf9, 0x18, 0x00, 0x00, 0x81, 0x31, 0xfe,
+ 0x45, 0x4a, 0x32, 0x44, 0x30, 0x38, 0x31, 0x5f, 0x50, 0x56,
+ 0xb6 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 8,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 5,
+ .ar_t1_ifsc = 254
+ }, {
+ .ar_test = "Actual ATR (3)",
+ .ar_len = 22,
+ .ar_buf = { 0x3b, 0xfc, 0x18, 0x00, 0x00, 0x81, 0x31, 0x80,
+ 0x45, 0x90, 0x67, 0x46, 0x4a, 0x00, 0x64, 0x16, 0x6, 0xf2,
+ 0x72, 0x7e, 0x00, 0xe0 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 8,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 5,
+ .ar_t1_ifsc = 128
+ }, {
+ .ar_test = "Minimal T=1",
+ .ar_len = 4,
+ .ar_buf = { 0x3b, 0x80, 0x01, 0x81 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1 Fi/Di",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x90, 0x34, 0x01, 0xa5 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 3,
+ .ar_di = 4,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1 TA2 says neg, T=1 def",
+ .ar_len = 5,
+ .ar_buf = { 0x3b, 0x80, 0x11, 0x11, 0x80 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says neg, T=0 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x10, 0x01, 0x01 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says neg, T=1 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x11, 0x01, 0x00 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says not neg, T=0 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x90, 0x01, 0x81 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T0,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=0, T=1 TA2 says not neg, T=1 def",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x90, 0x81, 0x01, 0x90 },
+ .ar_sup = ATR_P_T0 | ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_FALSE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t0_wi = 10,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1, BWI/CWI",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x21, 0x59, 0x79 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 5,
+ .ar_t1_cwi = 9,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1, IFSC",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x11, 0x49, 0x59 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 73
+ }, {
+ .ar_test = "T=1, Checksum (LRC)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x41, 0x00, 0x40 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }, {
+ .ar_test = "T=1, Checksum (CRC)",
+ .ar_len = 6,
+ .ar_buf = { 0x3b, 0x80, 0x81, 0x41, 0x01, 0x41 },
+ .ar_sup = ATR_P_T1,
+ .ar_def = ATR_P_T1,
+ .ar_neg = B_TRUE,
+ .ar_fi = 1,
+ .ar_di = 1,
+ .ar_conv = ATR_CONVENTION_DIRECT,
+ .ar_guard = 0,
+ .ar_stop = ATR_CLOCK_STOP_NONE,
+ .ar_t1_cksum = ATR_T1_CHECKSUM_CRC,
+ .ar_t1_bwi = 4,
+ .ar_t1_cwi = 13,
+ .ar_t1_ifsc = 32
+ }
+};
+
+static void
+atr_parse_failed(atr_test_t *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) fprintf(stderr, "Test \"%s\" failed: ", test->ar_test);
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static uint_t
+atr_parse_one(atr_data_t *data, atr_test_t *test)
+{
+ uint_t err = 0;
+ atr_parsecode_t ret;
+ atr_protocol_t sup, def;
+ boolean_t neg;
+ uint8_t fi, di, guard;
+ atr_convention_t conv;
+ atr_clock_stop_t stop;
+
+ ret = atr_parse(test->ar_buf, test->ar_len, data);
+ if (ret != test->ar_retval) {
+ atr_parse_failed(test, "found unexpected return "
+ "value: %u (%s), expected: %u", ret, atr_strerror(ret),
+ test->ar_retval);
+ return (1);
+ }
+
+ /* Don't test anything else if it's not OK */
+ if (ret != ATR_CODE_OK)
+ return (0);
+
+ sup = atr_supported_protocols(data);
+ def = atr_default_protocol(data);
+ neg = atr_params_negotiable(data);
+ fi = atr_fi_index(data);
+ di = atr_di_index(data);
+ conv = atr_convention(data);
+ guard = atr_extra_guardtime(data);
+ stop = atr_clock_stop(data);
+
+ if (sup != test->ar_sup) {
+ atr_parse_failed(test, "Found mismatched supported "
+ "protocols: %u, expected: %u", sup, test->ar_sup);
+ err++;
+ }
+
+ if (def != test->ar_def) {
+ atr_parse_failed(test, "Found mismatched default "
+ "protocols: %u, expected: %u", def, test->ar_def);
+ err++;
+ }
+
+ if (neg != test->ar_neg) {
+ atr_parse_failed(test, "Found mismatched negotiable bit: "
+ "%u, expected %u", neg, test->ar_neg);
+ err++;
+ }
+
+ if (fi != test->ar_fi) {
+ atr_parse_failed(test, "Found mismatched fi index: "
+ "%u, expected: %u", fi, test->ar_fi);
+ err++;
+ }
+
+ if (di != test->ar_di) {
+ atr_parse_failed(test, "Found mismatched di index: "
+ "%u, expected: %u", di, test->ar_di);
+ err++;
+ }
+
+ if (conv != test->ar_conv) {
+ atr_parse_failed(test, "Found mismatched TS convention: "
+ "%u, expected: %u", conv, test->ar_conv);
+ err++;
+ }
+
+ if (guard != test->ar_guard) {
+ atr_parse_failed(test, "Found mismatched extra guardtime: "
+ "%u, expected: %u", guard, test->ar_guard);
+ err++;
+ }
+
+ if (stop != test->ar_stop) {
+ atr_parse_failed(test, "Found mismatched clock stop: "
+ "%u, expected: %u", stop, test->ar_stop);
+ err++;
+ }
+
+ if ((sup & ATR_P_T0) != 0) {
+ uint8_t wi;
+
+ wi = atr_t0_wi(data);
+ if (wi != test->ar_t0_wi) {
+ atr_parse_failed(test, "Found mismatched T0 wi: "
+ "%u, expected: %u", wi, test->ar_t0_wi);
+ err++;
+ }
+ }
+
+ if ((sup & ATR_P_T1) != 0) {
+ atr_t1_checksum_t cksum;
+ uint8_t bwi, cwi, ifsc;
+
+ cksum = atr_t1_checksum(data);
+ bwi = atr_t1_bwi(data);
+ cwi = atr_t1_cwi(data);
+ ifsc = atr_t1_ifsc(data);
+
+ if (cksum != test->ar_t1_cksum) {
+ atr_parse_failed(test, "Found mistmatched T1 checksum: "
+ "%u, expected: %u", cksum, test->ar_t1_cksum);
+ err++;
+ }
+
+ if (bwi != test->ar_t1_bwi) {
+ atr_parse_failed(test, "Found mistmatched T1 bwi: "
+ "%u, expected: %u", bwi, test->ar_t1_bwi);
+ err++;
+ }
+
+ if (cwi != test->ar_t1_cwi) {
+ atr_parse_failed(test, "Found mistmatched T1 cwi: "
+ "%u, expected: %u", cwi, test->ar_t1_cwi);
+ err++;
+ }
+
+ if (ifsc != test->ar_t1_ifsc) {
+ atr_parse_failed(test, "Found mistmatched T1 ifsc: "
+ "%u, expected: %u", ifsc, test->ar_t1_ifsc);
+ err++;
+ }
+ }
+
+ if (err > 0) {
+ atr_data_dump(data, stderr);
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+main(void)
+{
+ uint_t i;
+ uint_t errs = 0;
+ atr_data_t *data;
+
+ data = atr_data_alloc();
+ if (data == NULL) {
+ errx(EXIT_FAILURE, "failed to allocate atr_data_t");
+ }
+
+ for (i = 0; i < sizeof (atr_tests) / sizeof (atr_test_t); i++) {
+ atr_data_reset(data);
+ errs += atr_parse_one(data, &atr_tests[i]);
+ }
+
+ atr_data_free(data);
+
+ if (errs != 0) {
+ warnx("%d test(s) failed", errs);
+ }
+ return (errs != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-badread.c b/usr/src/test/os-tests/tests/uccid/excl-badread.c
new file mode 100644
index 0000000000..e5f265d1e7
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-badread.c
@@ -0,0 +1,80 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify various bad read conditions fail successfully.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ uccid_cmd_txn_begin_t begin;
+ ssize_t ret;
+ char buf[500];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+
+ /*
+ * Read without having a transaction
+ */
+ ret = read(fd, buf, sizeof (buf));
+ if (ret != -1) {
+ errx(EXIT_FAILURE, "read succeeded when it should have failed "
+ "(EACCES case), returned %ld", ret);
+ }
+
+ if (errno != EACCES) {
+ errx(EXIT_FAILURE, "found wrong value for errno. Expected "
+ "%d, received %d", EACCES, errno);
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ ret = read(fd, buf, sizeof (buf));
+ if (ret != -1) {
+ errx(EXIT_FAILURE, "read succeeded when it should have failed "
+ "(ENODATA case), returned %ld", ret);
+ }
+
+ if (errno != ENODATA) {
+ errx(EXIT_FAILURE, "found wrong value for errno. Expected "
+ "%d, received %d", ENODATA, errno);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-basic.c b/usr/src/test/os-tests/tests/uccid/excl-basic.c
new file mode 100644
index 0000000000..c6cf30dd5e
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-basic.c
@@ -0,0 +1,65 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can grab a basic exclusive lock through an ioctl on the slot.
+ * Then that we can release it afterwards.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ uint_t i;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ for (i = 0; i < 10; i++) {
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-close.c b/usr/src/test/os-tests/tests/uccid/excl-close.c
new file mode 100644
index 0000000000..3936c73ab0
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-close.c
@@ -0,0 +1,81 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that if a child grabs an exclusive lock and calls exit, we can grab it
+ * again.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, estat;
+ pid_t pid;
+ uccid_cmd_txn_begin_t begin;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+
+ pid = fork();
+ if (pid == 0) {
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ _exit(0);
+ }
+
+ estat = -1;
+ if (waitpid(pid, &estat, 0) == -1) {
+ err(EXIT_FAILURE, "failed to wait for pid %d", pid);
+ }
+
+ if (estat != 0) {
+ errx(EXIT_FAILURE, "child exited with non-zero value (%d)",
+ estat);
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-loop.c b/usr/src/test/os-tests/tests/uccid/excl-loop.c
new file mode 100644
index 0000000000..f31fc81a34
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-loop.c
@@ -0,0 +1,84 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can grab a basic exclusive lock and then if we try to get
+ * another lock it fails. Regardless of whether we do so through open(2) or
+ * ioctl(2).
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/debug.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EEXIST);
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+
+ VERIFY0(close(fd));
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY0(ret);
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EEXIST);
+
+ VERIFY0(close(fd));
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-nonblock.c b/usr/src/test/os-tests/tests/uccid/excl-nonblock.c
new file mode 100644
index 0000000000..ee4f1edbd1
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-nonblock.c
@@ -0,0 +1,99 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that if we've grabbed an exclusive lock, another thread fails to grab
+ * it as a non-blocking lock.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/debug.h>
+#include <thread.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+void *
+nonblock_thread(void *arg)
+{
+ uccid_cmd_txn_begin_t begin;
+ int ret;
+ int fd = (uintptr_t)arg;
+
+
+ bzero(&begin, sizeof (begin));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ begin.uct_flags = UCCID_TXN_DONT_BLOCK;
+
+ ret = ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EBUSY);
+
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fda, fdb;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+ thread_t thr;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fda = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if ((fdb = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ if (ioctl(fda, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if (thr_create(NULL, 0, nonblock_thread, (void *)(uintptr_t)fdb, 0,
+ &thr) != 0) {
+ err(EXIT_FAILURE, "failed to create thread");
+ }
+
+ if (thr_join(thr, NULL, NULL) != 0) {
+ err(EXIT_FAILURE, "failed to join therad");
+ }
+
+ if (ioctl(fda, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/excl-reset.c b/usr/src/test/os-tests/tests/uccid/excl-reset.c
new file mode 100644
index 0000000000..7ab1718475
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/excl-reset.c
@@ -0,0 +1,65 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can grab a basic exclusive lock through an ioctl on the slot.
+ * Then that we can release it afterwards and reset the ICC.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ uint_t i;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_txn_end_t end;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ bzero(&end, sizeof (end));
+
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RESET;
+
+ for (i = 0; i < 10; i++) {
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/modify.c b/usr/src/test/os-tests/tests/uccid/modify.c
new file mode 100644
index 0000000000..1ac44f3ba4
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/modify.c
@@ -0,0 +1,181 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can issue ICC_MODIFY ioctls. Also, check some of the failure
+ * modes.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/debug.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ uccid_cmd_icc_modify_t uci;
+ uccid_cmd_status_t ucs;
+ uccid_cmd_txn_begin_t begin;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ /* try power off the card outside of a transaction */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EACCES);
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ /* make sure the card is active (power on) */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* power off the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is inactive now */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, ==, 0);
+
+ /* power on the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_ON;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is active again */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* do a warm reset of the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_WARM_RESET;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is still active */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* write a command to the card, which is assumed to be a YubiKey */
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) < 0) {
+ err(EXIT_FAILURE, "failed to write data");
+ }
+
+ /* power off the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is inactive now */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, ==, 0);
+
+ /* try to read the answer from the YubiKey. */
+ ret = read(fd, buf, sizeof (buf));
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, ENXIO);
+
+ /* power on the card */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_ON;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, 0);
+
+ /* make sure the card is active again */
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3U(ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE, !=, 0);
+
+ /* test various failure modes */
+ uci.uci_version = UCCID_VERSION_ONE - 1;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ uci.uci_version = UCCID_VERSION_ONE + 1;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = 0;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = -1;
+ ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/notxn-poll.c b/usr/src/test/os-tests/tests/uccid/notxn-poll.c
new file mode 100644
index 0000000000..b45f06b218
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/notxn-poll.c
@@ -0,0 +1,57 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that trying to poll without a transaction / excl access works but
+ * returns no events.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/debug.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLIN;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ VERIFY3S(ret, ==, 0);
+ VERIFY3S(pfds[0].revents, ==, 0);
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/pollin.c b/usr/src/test/os-tests/tests/uccid/pollin.c
new file mode 100644
index 0000000000..dd81c245cc
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/pollin.c
@@ -0,0 +1,64 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open up a device and make sure we get pollout by default.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+ uccid_cmd_txn_begin_t begin;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLIN;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 0) {
+ err(EXIT_FAILURE, "poll didn't return 0, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/pollout.c b/usr/src/test/os-tests/tests/uccid/pollout.c
new file mode 100644
index 0000000000..a9928a6fe2
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/pollout.c
@@ -0,0 +1,68 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open up a device and make sure we get pollout by default.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+ uccid_cmd_txn_begin_t begin;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLOUT;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if (!(pfds[0].revents & POLLOUT)) {
+ err(EXIT_FAILURE, "missing pollout, got %d", pfds[0].revents);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/status.c b/usr/src/test/os-tests/tests/uccid/status.c
new file mode 100644
index 0000000000..ae2a51226f
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/status.c
@@ -0,0 +1,97 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that we can issue various status ioctls regardless of whether or not
+ * we have exclusive access on our handle. Also, check some of the failure
+ * modes.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/debug.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, efd, ret;
+ uccid_cmd_status_t ucs;
+ uccid_cmd_txn_begin_t begin;
+ void *badaddr;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ if ((efd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(efd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+
+ ret = ioctl(efd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, 0);
+
+ ucs.ucs_version = UCCID_VERSION_ONE - 1;
+ ret = ioctl(efd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ ucs.ucs_version = UCCID_VERSION_ONE + 1;
+ ret = ioctl(efd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ ucs.ucs_version = UCCID_VERSION_ONE - 1;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ ucs.ucs_version = UCCID_VERSION_ONE + 1;
+ ret = ioctl(fd, UCCID_CMD_STATUS, &ucs);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, EINVAL);
+
+ badaddr = mmap(NULL, PAGESIZE, PROT_READ, MAP_PRIVATE | MAP_ANON, -1,
+ 0);
+ VERIFY3P(badaddr, !=, MAP_FAILED);
+ VERIFY0(munmap(badaddr, PAGESIZE));
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/txn-pollerr.c b/usr/src/test/os-tests/tests/uccid/txn-pollerr.c
new file mode 100644
index 0000000000..b19598f711
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/txn-pollerr.c
@@ -0,0 +1,88 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Verify that closing a transaction while polling generates POLLERR.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/debug.h>
+#include <poll.h>
+#include <port.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd, port;
+ uccid_cmd_txn_end_t end;
+ uccid_cmd_txn_begin_t begin;
+ port_event_t pe;
+ timespec_t to;
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((port = port_create()) == -1) {
+ err(EXIT_FAILURE, "failed to create event port: %d",
+ port);
+ }
+
+ if ((fd = open(argv[1], O_RDWR | O_EXCL)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ /*
+ * Do not poll for pollout here, since by default, after grabbing a
+ * transaction, the device is writeable.
+ */
+ if (port_associate(port, PORT_SOURCE_FD, fd, POLLIN, NULL) != 0) {
+ err(EXIT_FAILURE, "failed to associate");
+ }
+
+ bzero(&end, sizeof (end));
+ end.uct_version = UCCID_CURRENT_VERSION;
+ end.uct_flags = UCCID_TXN_END_RELEASE;
+
+ if (ioctl(fd, UCCID_CMD_TXN_END, &end) != 0) {
+ err(EXIT_FAILURE, "failed to issue end ioctl");
+ }
+
+ bzero(&to, sizeof (timespec_t));
+ if (port_get(port, &pe, &to) != 0) {
+ err(EXIT_FAILURE, "failed to port_get()");
+ }
+
+ VERIFY3S(pe.portev_source, ==, PORT_SOURCE_FD);
+ VERIFY3S(pe.portev_object, ==, fd);
+ VERIFY3S(pe.portev_events & POLLERR, !=, 0);
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/yk-poll.c b/usr/src/test/os-tests/tests/uccid/yk-poll.c
new file mode 100644
index 0000000000..fb52949f38
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/yk-poll.c
@@ -0,0 +1,108 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open a YubiKey class device and get the basic information applet
+ * through an APDU while using poll(2) to check device readyness.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ struct pollfd pfds[1];
+ uccid_cmd_txn_begin_t begin;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLOUT | POLLIN | POLLRDNORM;
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if ((pfds[0].revents & POLLOUT) != POLLOUT) {
+ err(EXIT_FAILURE, "expecting pollout, got %d", pfds[0].revents);
+ }
+
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) < 0) {
+ err(EXIT_FAILURE, "failed to write data");
+ }
+
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, -1);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if ((pfds[0].revents & (POLLIN | POLLRDNORM)) !=
+ (POLLIN | POLLRDNORM)) {
+ err(EXIT_FAILURE, "expecting pollin|pollrdnorm, got %d",
+ pfds[0].revents);
+ }
+
+ if ((ret = read(fd, buf, sizeof (buf))) < 0) {
+ err(EXIT_FAILURE, "failed to read data");
+ }
+
+ pfds[0].revents = 0;
+
+ ret = poll(pfds, 1, 0);
+ if (ret != 1) {
+ err(EXIT_FAILURE, "poll didn't return 1, returned %d "
+ "(errno %d)", ret, errno);
+ }
+
+ if ((pfds[0].revents & POLLOUT) != POLLOUT) {
+ err(EXIT_FAILURE, "expecting pollout, got %d", pfds[0].revents);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/yk-readonly.c b/usr/src/test/os-tests/tests/uccid/yk-readonly.c
new file mode 100644
index 0000000000..dfe7390ea1
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/yk-readonly.c
@@ -0,0 +1,84 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Open a YubiKey class device read-only and try to get the basic information
+ * applet through an APDU, which should fail. Try to get the status, which
+ * should succeed, and attempt to power off, which should fail.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd, ret;
+ uccid_cmd_icc_modify_t uci;
+ uccid_cmd_txn_begin_t begin;
+ uccid_cmd_status_t ucs;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) == 0) {
+ err(EXIT_FAILURE, "didn't fail to issue begin ioctl");
+ }
+
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) != -1) {
+ err(EXIT_FAILURE, "didn't fail to write data");
+ }
+
+ if ((ret = read(fd, buf, sizeof (buf))) != -1) {
+ err(EXIT_FAILURE, "didn't fail to read data");
+ }
+
+ bzero(&ucs, sizeof (ucs));
+ ucs.ucs_version = UCCID_CURRENT_VERSION;
+ if ((ret = ioctl(fd, UCCID_CMD_STATUS, &ucs)) != 0) {
+ err(EXIT_FAILURE, "failed to get status");
+ }
+
+ /* try power off the card outside of a transaction */
+ bzero(&uci, sizeof (uci));
+ uci.uci_version = UCCID_CURRENT_VERSION;
+ uci.uci_action = UCCID_ICC_POWER_OFF;
+ if ((ret = ioctl(fd, UCCID_CMD_ICC_MODIFY, &uci)) == 0) {
+ err(EXIT_FAILURE, "didn't fail to power off ICC");
+ }
+
+
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/uccid/yk.c b/usr/src/test/os-tests/tests/uccid/yk.c
new file mode 100644
index 0000000000..45bdfd059e
--- /dev/null
+++ b/usr/src/test/os-tests/tests/uccid/yk.c
@@ -0,0 +1,78 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+/*
+ * Attempt to open a YubiKey class device and get the basic information applet
+ * through an APDU.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/usb/clients/ccid/uccid.h>
+
+static const uint8_t yk_req[] = {
+ 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
+};
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ ssize_t ret, i;
+ uccid_cmd_txn_begin_t begin;
+ uint8_t buf[UCCID_APDU_SIZE_MAX];
+
+ if (argc != 2) {
+ errx(EXIT_FAILURE, "missing required ccid path");
+ }
+
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", argv[1]);
+ }
+
+ bzero(&begin, sizeof (begin));
+ begin.uct_version = UCCID_CURRENT_VERSION;
+
+ if (ioctl(fd, UCCID_CMD_TXN_BEGIN, &begin) != 0) {
+ err(EXIT_FAILURE, "failed to issue begin ioctl");
+ }
+
+ if ((ret = write(fd, yk_req, sizeof (yk_req))) < 0) {
+ err(EXIT_FAILURE, "failed to write data");
+ }
+
+ if ((ret = read(fd, buf, sizeof (buf))) < 0) {
+ err(EXIT_FAILURE, "failed to read data");
+ }
+
+ (void) printf("read %d bytes\n", ret);
+ for (i = 0; i < ret; i++) {
+ (void) printf("%02x", buf[i]);
+ if (i == (ret - 1) || (i % 16) == 15) {
+ (void) printf("\n");
+ } else {
+ (void) printf(" ");
+ }
+ }
+
+ return (0);
+}