예제 #1
0
/* Reset reader */
static int ctapi_reset(sc_reader_t *reader)
{
	struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
	char rv;
	u8 cmd[5], rbuf[256], sad, dad;
	unsigned short lr;

	cmd[0] = CTBCS_CLA;
	cmd[1] = CTBCS_INS_RESET;
	cmd[2] = priv->slot ? CTBCS_P1_INTERFACE1 + priv->slot : CTBCS_P1_CT_KERNEL;
	cmd[3] = 0x00; /* No response. We might also use 0x01 (return ATR) or 0x02 (return historical bytes) here */
	cmd[4] = 0x00;
	dad = 1;
	sad = 2;
	lr = 256;

	rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
	if (rv || (lr < 2)) {
		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error getting status of terminal: %d, using defaults\n", rv);
		return SC_ERROR_TRANSMIT_FAILED;
	}
	if (rbuf[lr-2] != 0x90) {
		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "SW1/SW2: 0x%x/0x%x\n", rbuf[lr-2], rbuf[lr-1]);
		return SC_ERROR_TRANSMIT_FAILED;
	}
	return 0;
}
예제 #2
0
static int ctapi_internal_transmit(sc_reader_t *reader,
			 const u8 *sendbuf, size_t sendsize,
			 u8 *recvbuf, size_t *recvsize,
			 unsigned long control)
{
	struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
	u8 dad, sad;
	unsigned short lr;
	char rv;
	
	if (control)
		dad = 1;
	else
		dad = 0;

	sad = 2;
	lr = *recvsize;
	
	rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, (unsigned short)sendsize, (u8 *) sendbuf, &lr, recvbuf);
	if (rv != 0) {
		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error transmitting APDU: %d\n", rv);
		return SC_ERROR_TRANSMIT_FAILED;
	}
	*recvsize = lr;
	
	return 0;
}
static int pcsc_internal_transmit(sc_reader_t *reader, sc_slot_info_t *slot,
			 const u8 *sendbuf, size_t sendsize,
			 u8 *recvbuf, size_t *recvsize,
			 unsigned long control)
{
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
	SCARD_IO_REQUEST sSendPci, sRecvPci;
	DWORD dwSendLength, dwRecvLength;
	LONG rv;
	SCARDHANDLE card;
	struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);

	SC_FUNC_CALLED(reader->ctx, 3);
	assert(pslot != NULL);
	card = pslot->pcsc_card;

	sSendPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol);
	sSendPci.cbPciLength = sizeof(sSendPci);
	sRecvPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol);
	sRecvPci.cbPciLength = sizeof(sRecvPci);

	dwSendLength = sendsize;
	dwRecvLength = *recvsize;

	if (!control) {
		rv = priv->gpriv->SCardTransmit(card, &sSendPci, sendbuf, dwSendLength,
				   &sRecvPci, recvbuf, &dwRecvLength);
	} else {
		if (priv->gpriv->SCardControlOLD != NULL) {
			rv = priv->gpriv->SCardControlOLD(card, sendbuf, dwSendLength,
				  recvbuf, &dwRecvLength);
		}
		else {
			rv = priv->gpriv->SCardControl(card, (DWORD) control, sendbuf, dwSendLength,
				  recvbuf, dwRecvLength, &dwRecvLength);
		}
	}

	if (rv != SCARD_S_SUCCESS) {
		switch (rv) {
		case SCARD_W_REMOVED_CARD:
			return SC_ERROR_CARD_REMOVED;
		case SCARD_E_NOT_TRANSACTED:
			if (!(pcsc_detect_card_presence(reader, slot) & SC_SLOT_CARD_PRESENT))
				return SC_ERROR_CARD_REMOVED;
			return SC_ERROR_TRANSMIT_FAILED;
		default:
			/* Windows' PC/SC returns 0x8010002f (??) if a card is removed */
			if (pcsc_detect_card_presence(reader, slot) != 1)
				return SC_ERROR_CARD_REMOVED;
			PCSC_ERROR(reader->ctx, "SCardTransmit failed", rv);
			return SC_ERROR_TRANSMIT_FAILED;
		}
	}
	if (!control && dwRecvLength < 2)
		return SC_ERROR_UNKNOWN_DATA_RECEIVED;
	*recvsize = dwRecvLength;

	return SC_SUCCESS;
}
예제 #4
0
static int ctapi_connect(sc_reader_t *reader)
{
	struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
	char rv;
	u8 cmd[9], rbuf[256], sad, dad;
	unsigned short lr;
	int r;

	cmd[0] = CTBCS_CLA;
	cmd[1] = CTBCS_INS_REQUEST;
	cmd[2] = CTBCS_P1_INTERFACE1;
	cmd[3] = CTBCS_P2_REQUEST_GET_ATR;
	cmd[4] = 0x00;
	dad = 1;
	sad = 2;
	lr = 256;

	rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
	if (rv || rbuf[lr-2] != 0x90) {
		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error activating card: %d\n", rv);
		return SC_ERROR_TRANSMIT_FAILED;
	}
	if (lr < 2)
		SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
	lr -= 2;
	if (lr > SC_MAX_ATR_SIZE)
		return SC_ERROR_INTERNAL;
	reader->atr.len = lr;
	memcpy(reader->atr.value, rbuf, lr);
	r = _sc_parse_atr(reader);

	return 0;
}
예제 #5
0
static int ctapi_release(sc_reader_t *reader)
{
	struct ctapi_private_data *priv = GET_PRIV_DATA(reader);

	priv->funcs.CT_close(priv->ctn);

	free(priv);
	return 0;
}
static int pcsc_disconnect(sc_reader_t * reader, sc_slot_info_t * slot)
{
	struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);

	priv->gpriv->SCardDisconnect(pslot->pcsc_card, priv->gpriv->connect_reset ?
	          SCARD_RESET_CARD : SCARD_LEAVE_CARD);
	memset(pslot, 0, sizeof(*pslot));
	slot->flags = 0;
	return SC_SUCCESS;
}
예제 #7
0
static int ctapi_release(sc_reader_t *reader)
{
	struct ctapi_private_data *priv = GET_PRIV_DATA(reader);


	if (!(reader->ctx->flags & SC_CTX_FLAG_TERMINATE))
		priv->funcs.CT_close(priv->ctn);

	free(priv);
	return 0;
}
static int pcsc_release(sc_reader_t *reader)
{
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);

	free(priv->reader_name);
	free(priv);
	if (reader->slot[0].drv_data != NULL) {
		free(reader->slot[0].drv_data);
		reader->slot[0].drv_data = NULL;
	}
	return SC_SUCCESS;
}
static int pcsc_unlock(sc_reader_t *reader, sc_slot_info_t *slot)
{
	long rv;
	struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);

	SC_FUNC_CALLED(reader->ctx, 3);
	assert(pslot != NULL);

	rv = priv->gpriv->SCardEndTransaction(pslot->pcsc_card, priv->gpriv->transaction_reset ?
                           SCARD_RESET_CARD : SCARD_LEAVE_CARD);

	pslot->locked = 0;
	if (rv != SCARD_S_SUCCESS) {
		PCSC_ERROR(reader->ctx, "SCardEndTransaction failed", rv);
		return pcsc_ret_to_error(rv);
	}
	return SC_SUCCESS;
}
static int pcsc_reconnect(sc_reader_t * reader, sc_slot_info_t * slot, int reset)
{
	DWORD active_proto, protocol;
	LONG rv;
	struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
	int r;

	sc_debug(reader->ctx, "Reconnecting to the card...");

	r = refresh_slot_attributes(reader, slot);
	if (r)
		return r;
	if (!(slot->flags & SC_SLOT_CARD_PRESENT))
		return SC_ERROR_CARD_NOT_PRESENT;

	/* reconnect always unlocks transaction */
	pslot->locked = 0;
	
	rv = priv->gpriv->SCardReconnect(pslot->pcsc_card,
			    priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
			    SCARD_PROTOCOL_ANY, reset ? SCARD_UNPOWER_CARD : SCARD_LEAVE_CARD, &active_proto);

	/* Check for protocol difference */
	if (rv == SCARD_S_SUCCESS && _sc_check_forced_protocol
	    (reader->ctx, slot->atr, slot->atr_len,
	     (unsigned int *)&protocol)) {
		protocol = opensc_proto_to_pcsc(protocol);
		if (pcsc_proto_to_opensc(active_proto) != protocol) {
		 rv = priv->gpriv->SCardReconnect(pslot->pcsc_card,
		 		     priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
		 		     protocol, SCARD_UNPOWER_CARD, &active_proto);
		}
	}

	if (rv != SCARD_S_SUCCESS) {
		PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv);
		return rv;
	}
	
	slot->active_protocol = pcsc_proto_to_opensc(active_proto);
	return rv;
}
예제 #11
0
static int refresh_attributes(sc_reader_t *reader)
{
	struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
	char rv;
	u8 cmd[5], rbuf[256], sad, dad;
	unsigned short lr;

	if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE)
		return SC_ERROR_NOT_ALLOWED;

	cmd[0] = CTBCS_CLA;
	cmd[1] = CTBCS_INS_STATUS;
	cmd[2] = CTBCS_P1_CT_KERNEL;
	cmd[3] = CTBCS_P2_STATUS_ICC;
	cmd[4] = 0x00;
	dad = 1;
	sad = 2;
	lr = 256;

	reader->flags = 0;

	rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
	if (rv || (lr < 3) || (rbuf[lr-2] != 0x90)) {
		sc_log(reader->ctx, "Error getting status of terminal: %d/%d/0x%x", rv, lr, rbuf[lr-2]);
		return SC_ERROR_TRANSMIT_FAILED;
	}
	if (lr < 4) {
		if (rbuf[0] & CTBCS_DATA_STATUS_CARD)
			reader->flags = SC_READER_CARD_PRESENT;
	} else {
		if (rbuf[0] != CTBCS_P2_STATUS_ICC) {
			/* Should we be more tolerant here? I do not think so... */
			sc_log(reader->ctx, "Invalid data object returned on CTBCS_P2_STATUS_ICC: 0x%x", rbuf[0]);
		return SC_ERROR_TRANSMIT_FAILED;
		}
		/* Fixme - should not be reached */
		sc_log(reader->ctx, "Returned status for  %d slots", rbuf[1]);
		reader->flags = SC_READER_CARD_PRESENT;
	}

	return 0;
}
static int pcsc_lock(sc_reader_t *reader, sc_slot_info_t *slot)
{
	long rv;
	struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);

	SC_FUNC_CALLED(reader->ctx, 3);
	assert(pslot != NULL);

	rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card);

	switch (rv) {
		case SCARD_E_INVALID_HANDLE:
		case SCARD_E_READER_UNAVAILABLE:
			rv = pcsc_connect(reader, slot);
			if (rv != SCARD_S_SUCCESS) {
				PCSC_ERROR(reader->ctx, "SCardConnect failed", rv);
				return pcsc_ret_to_error(rv);
			}
			/* return failure so that upper layers will be notified and try to lock again */
			return SC_ERROR_READER_REATTACHED;
		case SCARD_W_RESET_CARD:
			/* try to reconnect if the card was reset by some other application */
			rv = pcsc_reconnect(reader, slot, 0);
			if (rv != SCARD_S_SUCCESS) {
				PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv);
				return pcsc_ret_to_error(rv);
			}
			/* return failure so that upper layers will be notified and try to lock again */
			return SC_ERROR_CARD_RESET;
		case SCARD_S_SUCCESS:
			pslot->locked = 1;
			return SC_SUCCESS;
		default:
			PCSC_ERROR(reader->ctx, "SCardBeginTransaction failed", rv);
			return pcsc_ret_to_error(rv);
	}
}
static int pcsc_connect(sc_reader_t *reader, sc_slot_info_t *slot)
{
	DWORD active_proto, protocol;
	SCARDHANDLE card_handle;
	LONG rv;
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
	struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
	int r;
	u8 feature_buf[256], rbuf[SC_MAX_APDU_BUFFER_SIZE];
	size_t rcount;
	DWORD i, feature_len, display_ioctl = 0;
	PCSC_TLV_STRUCTURE *pcsc_tlv;

	r = refresh_slot_attributes(reader, slot);
	if (r)
		return r;
	if (!(slot->flags & SC_SLOT_CARD_PRESENT))
		return SC_ERROR_CARD_NOT_PRESENT;

	/* Always connect with whatever protocol possible */
	rv = priv->gpriv->SCardConnect(priv->gpriv->pcsc_ctx, priv->reader_name,
			  priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
			  SCARD_PROTOCOL_ANY, &card_handle, &active_proto);
	if (rv != SCARD_S_SUCCESS) {
		PCSC_ERROR(reader->ctx, "SCardConnect failed", rv);
		return pcsc_ret_to_error(rv);
	}
	slot->active_protocol = pcsc_proto_to_opensc(active_proto);
	pslot->pcsc_card = card_handle;

	/* after connect reader is not locked yet */
	pslot->locked = 0;
	sc_debug(reader->ctx, "After connect protocol = %d", slot->active_protocol);
	
	/* If we need a specific protocol, reconnect if needed */
	if (_sc_check_forced_protocol(reader->ctx, slot->atr, slot->atr_len, (unsigned int *) &protocol)) {
		/* If current protocol differs from the protocol we want to force */
		if (slot->active_protocol != protocol) {
			sc_debug(reader->ctx, "Protocol difference, forcing protocol (%d)", protocol);
			/* Reconnect with a reset. pcsc_reconnect figures out the right forced protocol */
			rv = pcsc_reconnect(reader, slot, 1);
			if (rv != SCARD_S_SUCCESS) {
				PCSC_ERROR(reader->ctx, "SCardReconnect (to force protocol) failed", rv);
				return pcsc_ret_to_error(rv);
			}
			sc_debug(reader->ctx, "Proto after reconnect = %d", slot->active_protocol);
		}
	}

	/* check for pinpad support */
	if (priv->gpriv->SCardControl != NULL) {
		sc_debug(reader->ctx, "Requesting reader features ... ");

		rv = priv->gpriv->SCardControl(pslot->pcsc_card, CM_IOCTL_GET_FEATURE_REQUEST, NULL,
				  0, feature_buf, sizeof(feature_buf), &feature_len);
		if (rv != SCARD_S_SUCCESS) {
			sc_debug(reader->ctx, "SCardControl failed %08x", rv);
		}
		else {
			if ((feature_len % sizeof(PCSC_TLV_STRUCTURE)) != 0) {
				sc_debug(reader->ctx, "Inconsistent TLV from reader!");
			}
			else {
				char *log_disabled = "but it's disabled in configuration file";
				/* get the number of elements instead of the complete size */
				feature_len /= sizeof(PCSC_TLV_STRUCTURE);

				pcsc_tlv = (PCSC_TLV_STRUCTURE *)feature_buf;
				for (i = 0; i < feature_len; i++) {
					if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT) {
						pslot->verify_ioctl = ntohl(pcsc_tlv[i].value);
					} else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_START) {
						pslot->verify_ioctl_start = ntohl(pcsc_tlv[i].value);
					} else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_FINISH) {
						pslot->verify_ioctl_finish = ntohl(pcsc_tlv[i].value);
					} else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT) {
						pslot->modify_ioctl = ntohl(pcsc_tlv[i].value);
					} else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_START) {
						pslot->modify_ioctl_start = ntohl(pcsc_tlv[i].value);
					} else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_FINISH) {
						pslot->modify_ioctl_finish = ntohl(pcsc_tlv[i].value);
					} else if (pcsc_tlv[i].tag == FEATURE_IFD_PIN_PROPERTIES) {
					        display_ioctl = ntohl(pcsc_tlv[i].value);
					} else {
						sc_debug(reader->ctx, "Reader feature %02x is not supported", pcsc_tlv[i].tag);
					}
				}
				
				/* Set slot capabilities based on detected IOCTLs */
				if (pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish)) {
					char *log_text = "Reader supports pinpad PIN verification";
					if (priv->gpriv->enable_pinpad) {
						sc_debug(reader->ctx, log_text);
						slot->capabilities |= SC_SLOT_CAP_PIN_PAD;
					} else {
						sc_debug(reader->ctx, "%s %s", log_text, log_disabled);
					}
				}
				
				if (pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish)) {
					char *log_text = "Reader supports pinpad PIN modification";
					if (priv->gpriv->enable_pinpad) {
						sc_debug(reader->ctx, log_text);
						slot->capabilities |= SC_SLOT_CAP_PIN_PAD;
					} else {
						sc_debug(reader->ctx, "%s %s", log_text, log_disabled);
					}
				}

				if (display_ioctl) {
					rcount = sizeof(rbuf);
					r = pcsc_internal_transmit(reader, slot, NULL, 0, rbuf, &rcount, display_ioctl);
					if (r == SC_SUCCESS) {
						if (rcount != sizeof(PIN_PROPERTIES_STRUCTURE)) {
							PIN_PROPERTIES_STRUCTURE *caps = (PIN_PROPERTIES_STRUCTURE *)rbuf;
							if (caps->wLcdLayout > 0) {
								sc_debug(reader->ctx, "Reader has a display: %04X", caps->wLcdLayout);
								slot->capabilities |= SC_SLOT_CAP_DISPLAY;
							} else
								sc_debug(reader->ctx, "Reader does not have a display.");
						} else {
							sc_debug(reader->ctx, "Returned PIN properties structure has bad length (%d)", rcount);
						}
					}
				}
			}
		}
	}
	return SC_SUCCESS;
}
/* Wait for an event to occur.
 * This function ignores the list of slots, because with
 * pcsc we have a 1:1 mapping of readers and slots anyway
 */
static int pcsc_wait_for_event(sc_reader_t **readers,
			       sc_slot_info_t **slots,
			       size_t nslots,
                               unsigned int event_mask,
                               int *reader,
			       unsigned int *event, int timeout)
{
	struct pcsc_private_data *priv = GET_PRIV_DATA(readers[0]);
	sc_context_t *ctx;
	SCARDCONTEXT pcsc_ctx;
	LONG ret;
	SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS];
	unsigned long on_bits, off_bits;
	time_t end_time, now, delta;
	size_t i;

	/* Prevent buffer overflow */
	if (nslots >= SC_MAX_READERS)
		return SC_ERROR_INVALID_ARGUMENTS;

	on_bits = off_bits = 0;
	if (event_mask & SC_EVENT_CARD_INSERTED) {
		event_mask &= ~SC_EVENT_CARD_INSERTED;
		on_bits |= SCARD_STATE_PRESENT;
	}
	if (event_mask & SC_EVENT_CARD_REMOVED) {
		event_mask &= ~SC_EVENT_CARD_REMOVED;
		off_bits |= SCARD_STATE_PRESENT;
	}
	if (event_mask != 0)
		return SC_ERROR_INVALID_ARGUMENTS;

	/* Find out the current status */
	ctx = readers[0]->ctx;
	pcsc_ctx = priv->gpriv->pcsc_ctx;
	for (i = 0; i < nslots; i++) {
		struct pcsc_private_data *priv2 = GET_PRIV_DATA(readers[i]);

		rgReaderStates[i].szReader = priv2->reader_name;
		rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE;
		rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE;

		/* Can we handle readers from different PCSC contexts? */
		if (priv2->gpriv->pcsc_ctx != pcsc_ctx)
			return SC_ERROR_INVALID_ARGUMENTS;
	}

	ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 0, rgReaderStates, nslots);
	if (ret != SCARD_S_SUCCESS) {
		PCSC_ERROR(ctx, "SCardGetStatusChange(1) failed", ret);
		return pcsc_ret_to_error(ret);
	}

	time(&now);
	end_time = now + (timeout + 999) / 1000;

	/* Wait for a status change and return if it's a card insert/removal
	 */
	for( ; ; ) {
		SCARD_READERSTATE_A *rsp;

		/* Scan the current state of all readers to see if they
		 * match any of the events we're polling for */
		*event = 0;
		for (i = 0, rsp = rgReaderStates; i < nslots; i++, rsp++) {
			unsigned long state, prev_state;

			prev_state = rsp->dwCurrentState;
			state = rsp->dwEventState;
			if ((state & on_bits & SCARD_STATE_PRESENT) &&
			    (prev_state & SCARD_STATE_EMPTY))
				*event |= SC_EVENT_CARD_INSERTED;
			if ((~state & off_bits & SCARD_STATE_PRESENT) &&
			    (prev_state & SCARD_STATE_PRESENT))
				*event |= SC_EVENT_CARD_REMOVED;
			if (*event) {
				*reader = i;
				return SC_SUCCESS;
			}

			/* No match - copy the state so pcscd knows
			 * what to watch out for */
			rsp->dwCurrentState = rsp->dwEventState;
		}

		/* Set the timeout if caller wants to time out */
		if (timeout == 0)
			return SC_ERROR_EVENT_TIMEOUT;
		if (timeout > 0) {
			time(&now);
			if (now >= end_time)
				return SC_ERROR_EVENT_TIMEOUT;
			delta = end_time - now;
		} else {
			delta = 3600;
		}

		ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 1000 * delta,
					   rgReaderStates, nslots);
		if (ret == (LONG) SCARD_E_TIMEOUT) {
			if (timeout < 0)
				continue;
			return SC_ERROR_EVENT_TIMEOUT;
		}
		if (ret != SCARD_S_SUCCESS) {
			PCSC_ERROR(ctx, "SCardGetStatusChange(2) failed", ret);
			return pcsc_ret_to_error(ret);
		}
	}
}
static int refresh_slot_attributes(sc_reader_t *reader, sc_slot_info_t *slot)
{
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
	struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
	LONG ret;

	SC_FUNC_CALLED(reader->ctx, 3);
	if (pslot->reader_state.szReader == NULL) {
		pslot->reader_state.szReader = priv->reader_name;
		pslot->reader_state.dwCurrentState = SCARD_STATE_UNAWARE;
		pslot->reader_state.dwEventState = SCARD_STATE_UNAWARE;
	} else {
		pslot->reader_state.dwCurrentState = pslot->reader_state.dwEventState;
	}

	ret = priv->gpriv->SCardGetStatusChange(priv->gpriv->pcsc_ctx, 0, &pslot->reader_state, 1);
	if (ret == (LONG)SCARD_E_TIMEOUT) { /* timeout: nothing changed */
		slot->flags &= ~SCARD_STATE_CHANGED;
		return 0;
	}
	if (ret != SCARD_S_SUCCESS) {
		PCSC_ERROR(reader->ctx, "SCardGetStatusChange failed", ret);
		return pcsc_ret_to_error(ret);
	}
	if (pslot->reader_state.dwEventState & SCARD_STATE_PRESENT) {
		int old_flags = slot->flags;
		int maybe_changed = 0;

		slot->flags |= SC_SLOT_CARD_PRESENT;
		slot->atr_len = pslot->reader_state.cbAtr;
		if (slot->atr_len > SC_MAX_ATR_SIZE)
			slot->atr_len = SC_MAX_ATR_SIZE;
		memcpy(slot->atr, pslot->reader_state.rgbAtr, slot->atr_len);

#ifndef _WIN32
		/* On Linux, SCARD_STATE_CHANGED always means there was an
		 * insert or removal. But we may miss events that way. */
		if (pslot->reader_state.dwEventState & SCARD_STATE_CHANGED) {
			slot->flags |= SC_SLOT_CARD_CHANGED;
		} else {
			maybe_changed = 1;
		}
#else
		/* On windows, SCARD_STATE_CHANGED is turned on by lots of
		 * other events, so it gives us a lot of false positives.
		 * But if it's off, there really was no change */
		if (pslot->reader_state.dwEventState & SCARD_STATE_CHANGED) {
			maybe_changed = 1;
		}
#endif
		/* If we aren't sure if the card state changed, check if
		 * the card handle is still valid. If the card changed,
		 * the handle will be invalid. */
		slot->flags &= ~SC_SLOT_CARD_CHANGED;
		if (maybe_changed) {
			if (old_flags & SC_SLOT_CARD_PRESENT) {
				DWORD readers_len = 0, state, prot, atr_len = SC_MAX_ATR_SIZE;
				unsigned char atr[SC_MAX_ATR_SIZE];
				LONG rv = priv->gpriv->SCardStatus(pslot->pcsc_card, NULL, &readers_len,
					&state,	&prot, atr, &atr_len);
				if (rv == (LONG)SCARD_W_REMOVED_CARD)
					slot->flags |= SC_SLOT_CARD_CHANGED;
			}
			else
				slot->flags |= SC_SLOT_CARD_CHANGED;
		}
	} else {
		slot->flags &= ~(SC_SLOT_CARD_PRESENT|SC_SLOT_CARD_CHANGED);
	}
	return 0;
}
/* Do the PIN command */
static int
part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot,
	     struct sc_pin_cmd_data *data)
{
	struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
	u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE];
	char dbuf[SC_MAX_APDU_BUFFER_SIZE * 3];
	size_t rcount = sizeof(rbuf), scount = 0;
	int r;
	DWORD ioctl = 0;
	sc_apdu_t *apdu;
	struct pcsc_slot_data *pslot = (struct pcsc_slot_data *) slot->drv_data;

	SC_FUNC_CALLED(reader->ctx, 3);
	assert(pslot != NULL);

	if (priv->gpriv->SCardControl == NULL)
		return SC_ERROR_NOT_SUPPORTED;

	/* The APDU must be provided by the card driver */
	if (!data->apdu) {
		sc_error(reader->ctx, "No APDU provided for PC/SC v2 pinpad verification!");
		return SC_ERROR_NOT_SUPPORTED;
	}

	apdu = data->apdu;
	switch (data->cmd) {
	case SC_PIN_CMD_VERIFY:
		if (!(pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish))) {
			sc_error(reader->ctx, "Pinpad reader does not support verification!");
			return SC_ERROR_NOT_SUPPORTED;
		}
		r = part10_build_verify_pin_block(sbuf, &scount, slot, data);
		ioctl = pslot->verify_ioctl ? pslot->verify_ioctl : pslot->verify_ioctl_start;
		break;
	case SC_PIN_CMD_CHANGE:
	case SC_PIN_CMD_UNBLOCK:
		if (!(pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish))) {
			sc_error(reader->ctx, "Pinpad reader does not support modification!");
			return SC_ERROR_NOT_SUPPORTED;
		}
		r = part10_build_modify_pin_block(sbuf, &scount, slot, data);
		ioctl = pslot->modify_ioctl ? pslot->modify_ioctl : pslot->modify_ioctl_start;
		break;
	default:
		sc_error(reader->ctx, "Unknown PIN command %d", data->cmd);
		return SC_ERROR_NOT_SUPPORTED;
	}

	/* If PIN block building failed, we fail too */
	SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad block building failed!");
	/* If not, debug it, just for fun */
	sc_bin_to_hex(sbuf, scount, dbuf, sizeof(dbuf), ':');
	sc_debug(reader->ctx, "PC/SC v2 pinpad block: %s", dbuf);

	r = pcsc_internal_transmit(reader, slot, sbuf, scount, rbuf, &rcount, ioctl);

	SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: block transmit failed!");
	/* finish the call if it was a two-phase operation */
	if ((ioctl == pslot->verify_ioctl_start)
	    || (ioctl == pslot->modify_ioctl_start)) {
		if (rcount != 0) {
			SC_FUNC_RETURN(reader->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED);
		}
		ioctl = (ioctl == pslot->verify_ioctl_start) ? pslot->verify_ioctl_finish : pslot->modify_ioctl_finish;

		rcount = sizeof(rbuf);
		r = pcsc_internal_transmit(reader, slot, sbuf, 0, rbuf, &rcount, ioctl);
		SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: finish operation failed!");
	}

	/* We expect only two bytes of result data (SW1 and SW2) */
	if (rcount != 2) {
		SC_FUNC_RETURN(reader->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	}

	/* Extract the SWs for the result APDU */
	apdu->sw1 = (unsigned int) rbuf[rcount - 2];
	apdu->sw2 = (unsigned int) rbuf[rcount - 1];

	r = SC_SUCCESS;
	switch (((unsigned int) apdu->sw1 << 8) | apdu->sw2) {
	case 0x6400: /* Input timed out */
		r = SC_ERROR_KEYPAD_TIMEOUT;
		break;
	case 0x6401: /* Input cancelled */
		r = SC_ERROR_KEYPAD_CANCELLED;
		break;
	case 0x6402: /* PINs don't match */
		r = SC_ERROR_KEYPAD_PIN_MISMATCH;
		break;
	case 0x6B80: /* Wrong data in the buffer, rejected by firmware */
		r = SC_ERROR_READER;
		break;
	}

	SC_TEST_RET(reader->ctx, r, "PIN command failed");

	/* PIN command completed, all is good */
	return SC_SUCCESS;
}