static int asepcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *pdata,
	int *tries_left)
{
	sc_apdu_t apdu;
	int r = SC_SUCCESS;
	u8  sbuf[SC_MAX_APDU_BUFFER_SIZE];

	if (tries_left)
		*tries_left = -1;

	/* only PIN verification is supported at the moment  */

	/* check PIN length */
	if (pdata->pin1.len < 4 || pdata->pin1.len > 16) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN1 length");
		return SC_ERROR_INVALID_PIN_LENGTH; 
	}

	switch (pdata->cmd) {
	case SC_PIN_CMD_VERIFY:
		if (pdata->pin_type != SC_AC_CHV && pdata->pin_type != SC_AC_AUT)
			return SC_ERROR_INVALID_ARGUMENTS;
		/* 'AUT' key is the transport PIN and should have reference '0' */
		if (pdata->pin_type == SC_AC_AUT && pdata->pin_reference)
			return SC_ERROR_INVALID_ARGUMENTS;
		/* build verify APDU and send it to the card */
		r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_VERIFY, 0);
		if (r != SC_SUCCESS)
			break;
		r = sc_transmit_apdu(card, &apdu);
		if (r != SC_SUCCESS)
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed");
		break;
	case SC_PIN_CMD_CHANGE:
		if (pdata->pin_type != SC_AC_CHV)
			return SC_ERROR_INVALID_ARGUMENTS;
		if (pdata->pin2.len < 4 || pdata->pin2.len > 16) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN2 length");
			return SC_ERROR_INVALID_PIN_LENGTH; 
		}
		/* 1. step: verify the old pin */
		r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_VERIFY, 0);
		if (r != SC_SUCCESS)
			break;
		r = sc_transmit_apdu(card, &apdu);
		if (r != SC_SUCCESS) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed");
			break;
		}
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) {
			/* unable to verify the old PIN */
			break;
		}
		/* 2, step: use CHANGE KEY to update the PIN */
		r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_CHANGE, 0);
		if (r != SC_SUCCESS)
			break;
		r = sc_transmit_apdu(card, &apdu);
		if (r != SC_SUCCESS)
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed");
		break;
	case SC_PIN_CMD_UNBLOCK:
		if (pdata->pin_type != SC_AC_CHV)
			return SC_ERROR_INVALID_ARGUMENTS;
		if (pdata->pin2.len < 4 || pdata->pin2.len > 16) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid PIN2 length");
			return SC_ERROR_INVALID_PIN_LENGTH; 
		}
		/* 1. step: verify the puk */
		r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_VERIFY, 1);
		if (r != SC_SUCCESS)
			break;
		r = sc_transmit_apdu(card, &apdu);
		if (r != SC_SUCCESS) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed");
			break;
		}
		/* 2, step: unblock and change the pin */
		r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_UNBLOCK, 0);
		if (r != SC_SUCCESS)
			break;
		r = sc_transmit_apdu(card, &apdu);
		if (r != SC_SUCCESS) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed");
			break;
		}
		break;
	default:
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "error: unknown cmd type");
		return SC_ERROR_INTERNAL;
	}
	/* Clear the buffer - it may contain pins */
	sc_mem_clear(sbuf, sizeof(sbuf));
	/* check for remaining tries if verification failed */
	if (r == SC_SUCCESS) {
		if (apdu.sw1 == 0x63) {
			if ((apdu.sw2 & 0xF0) == 0xC0 && tries_left != NULL)
				*tries_left = apdu.sw2 & 0x0F;
			r = SC_ERROR_PIN_CODE_INCORRECT;
			return r;
		}
		r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	}

	return r;
}