Merge branch 'master' into master

This commit is contained in:
Philippe Teuwen 2017-02-17 10:15:04 +01:00 committed by GitHub
commit 732a282190
15 changed files with 148 additions and 60 deletions

View file

@ -234,7 +234,14 @@ ENDIF(WIN32)
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Near Field Communication (NFC) library") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Near Field Communication (NFC) library")
SET(CPACK_PACKAGE_VENDOR "Roel Verdult") SET(CPACK_PACKAGE_VENDOR "Roel Verdult")
SET(CPACK_PACKAGE_CONTACT "Roel Verdul <roel@libnfc.org>") SET(CPACK_PACKAGE_CONTACT "Roel Verdul <roel@libnfc.org>")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README")
#Readme file
IF(WIN32)
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README-Windows.md")
ELSE(WIN32)
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
ENDIF(WIN32)
SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING")
SET(CPACK_PACKAGE_INSTALL_DIRECTORY "libnfc") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "libnfc")
SET(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})

View file

@ -10,7 +10,7 @@ pkgconfig_DATA = libnfc.pc
EXTRA_DIST = \ EXTRA_DIST = \
CMakeLists.txt \ CMakeLists.txt \
Doxyfile \ Doxyfile \
README-Windows.txt \ README-Windows.md \
libnfc.conf.sample libnfc.conf.sample
CLEANFILES = Doxygen.log coverage.info libnfc.pc CLEANFILES = Doxygen.log coverage.info libnfc.pc

View file

@ -27,7 +27,7 @@ The official forum site is:
The official development site is: The official development site is:
https://github.com/nfc-tools/libnfc https://github.com/nfc-tools/libnfc
Important note: this file covers POSIX systems, for Windows please read README-Windows.txt Important note: this file covers POSIX systems, for Windows please read README-Windows.md
Requirements Requirements
============ ============

View file

@ -1,2 +1,3 @@
blacklist nfc blacklist nfc
blacklist pn533 blacklist pn533
blacklist pm533_usb

View file

@ -4,6 +4,7 @@ VERSION 1.7
EXPORTS EXPORTS
nfc_init nfc_init
nfc_exit nfc_exit
nfc_register_driver
nfc_open nfc_open
nfc_close nfc_close
nfc_abort_command nfc_abort_command
@ -17,7 +18,6 @@ EXPORTS
nfc_initiator_select_dep_target nfc_initiator_select_dep_target
nfc_initiator_poll_dep_target nfc_initiator_poll_dep_target
nfc_initiator_deselect_target nfc_initiator_deselect_target
nfc_initiator_poll_targets
nfc_initiator_transceive_bytes nfc_initiator_transceive_bytes
nfc_initiator_transceive_bits nfc_initiator_transceive_bits
nfc_initiator_transceive_bytes_timed nfc_initiator_transceive_bytes_timed
@ -36,11 +36,15 @@ EXPORTS
nfc_device_get_connstring nfc_device_get_connstring
nfc_device_get_supported_modulation nfc_device_get_supported_modulation
nfc_device_get_supported_baud_rate nfc_device_get_supported_baud_rate
nfc_device_get_supported_baud_rate_target_mode
nfc_device_set_property_int nfc_device_set_property_int
nfc_device_set_property_bool nfc_device_set_property_bool
iso14443a_crc iso14443a_crc
iso14443a_crc_append iso14443a_crc_append
iso14443b_crc
iso14443b_crc_append
iso14443a_locate_historical_bytes iso14443a_locate_historical_bytes
nfc_free
nfc_version nfc_version
nfc_device_get_information_about nfc_device_get_information_about
str_nfc_modulation_type str_nfc_modulation_type

View file

@ -28,7 +28,7 @@
* @file nfc.h * @file nfc.h
* @brief libnfc interface * @brief libnfc interface
* *
* Provide all usefull functions (API) to handle NFC devices. * Provide all useful functions (API) to handle NFC devices.
*/ */
#ifndef _LIBNFC_H_ #ifndef _LIBNFC_H_

View file

@ -77,6 +77,8 @@
const char *serial_ports_device_radix[] = { "tty.SLAB_USBtoUART", "tty.usbserial", "tty.usbmodem", NULL }; const char *serial_ports_device_radix[] = { "tty.SLAB_USBtoUART", "tty.usbserial", "tty.usbmodem", NULL };
# elif defined (__FreeBSD__) || defined (__OpenBSD__) || defined(__FreeBSD_kernel__) # elif defined (__FreeBSD__) || defined (__OpenBSD__) || defined(__FreeBSD_kernel__)
const char *serial_ports_device_radix[] = { "cuaU", "cuau", NULL }; const char *serial_ports_device_radix[] = { "cuaU", "cuau", NULL };
# elif defined (__NetBSD__)
const char *serial_ports_device_radix[] = { "tty0", "ttyC", "ttyS", "ttyU", "ttyY" , NULL };
# elif defined (__linux__) || defined (__CYGWIN__) # elif defined (__linux__) || defined (__CYGWIN__)
const char *serial_ports_device_radix[] = { "ttyUSB", "ttyS", "ttyACM", "ttyAMA", "ttyO", NULL }; const char *serial_ports_device_radix[] = { "ttyUSB", "ttyS", "ttyACM", "ttyAMA", "ttyO", NULL };
# else # else

View file

@ -81,32 +81,38 @@
#define TgGetTargetStatus 0x8A #define TgGetTargetStatus 0x8A
/** @note PN53x's normal frame: /** @note PN53x's normal frame:
* See the PN532 (firmware) user manual, section 6.2.1.1: Normal information
* frame, figure 13. Normal information frame, page 28 rev. 02 - 2007-11-07.
* *
* .-- Start * .-- Preamble
* | .-- Packet length * | .-- Start
* | | .-- Length checksum
* | | | .-- Direction (D4 Host to PN, D5 PN to Host)
* | | | | .-- Code
* | | | | | .-- Packet checksum
* | | | | | | .-- Postamble
* V | | | | | |
* ----- V V V V V V
* 00 FF 02 FE D4 02 2A 00
*/
/** @note PN53x's extended frame:
*
* .-- Start
* | .-- Fixed to FF to enable extended frame
* | | .-- Packet length * | | .-- Packet length
* | | | .-- Length checksum * | | | .-- Length checksum
* | | | | .-- Direction (D4 Host to PN, D5 PN to Host) * | | | | .-- Direction (D4 Host to PN, D5 PN to Host)
* | | | | | .-- Code * | | | | | .-- Code
* | | | | | | .-- Packet checksum * | | | | | | .-- Packet checksum
* | | | | | | | .-- Postamble * | | | | | | | .-- Postamble
* V V V | | | | | * | V | | | | | |
* ----- ----- ----- V V V V V * V ----- V V V V V V
* 00 FF FF FF 00 02 FE D4 02 2A 00 * 00 00 FF 02 FE D4 02 2A 00
*/
/** @note PN53x's extended frame:
* See the PN532 (firmware) user manual, section 6.2.1.2: Extended information
* frame, figure 14. Normal information frame, page 29 rev. 02 - 2007-11-07.
*
* .-- Preamble
* | .-- Start
* | | .-- Fixed to FF to enable extended frame
* | | | .-- Packet length
* | | | | .-- Length checksum
* | | | | | .-- Direction (D4 Host to PN, D5 PN to Host)
* | | | | | | .-- Code
* | | | | | | | .-- Packet checksum
* | | | | | | | | .-- Postamble
* | V V V | | | | |
* V ----- ----- ----- V V V V V
* 00 00 FF FF FF 00 02 FE D4 02 2A 00
*/ */
/** /**

View file

@ -59,7 +59,7 @@
# define IOCTL_CCID_ESCAPE_SCARD_CTL_CODE SCARD_CTL_CODE(3500) # define IOCTL_CCID_ESCAPE_SCARD_CTL_CODE SCARD_CTL_CODE(3500)
#elif defined(__APPLE__) #elif defined(__APPLE__)
# define IOCTL_CCID_ESCAPE_SCARD_CTL_CODE (((0x31) << 16) | ((3500) << 2)) # define IOCTL_CCID_ESCAPE_SCARD_CTL_CODE (((0x31) << 16) | ((3500) << 2))
#elif defined (__FreeBSD__) || defined (__OpenBSD__) #elif defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__NetBSD__)
# define IOCTL_CCID_ESCAPE_SCARD_CTL_CODE (((0x31) << 16) | ((3500) << 2)) # define IOCTL_CCID_ESCAPE_SCARD_CTL_CODE (((0x31) << 16) | ((3500) << 2))
#elif defined (__linux__) #elif defined (__linux__)
# include <reader.h> # include <reader.h>

View file

@ -59,6 +59,13 @@
// I2C address of the PN532 chip. // I2C address of the PN532 chip.
#define PN532_I2C_ADDR 0x24 #define PN532_I2C_ADDR 0x24
/*
* When sending lots of data, the pn532 occasionally fails to respond in time.
* Since it happens so rarely, lets try to fix it by re-sending the data. This
* define allows for fine tuning the number of retries.
*/
#define PN532_SEND_RETRIES 3
// Internal data structs // Internal data structs
const struct pn53x_io pn532_i2c_io; const struct pn53x_io pn532_i2c_io;
@ -67,13 +74,9 @@ struct pn532_i2c_data {
volatile bool abort_flag; volatile bool abort_flag;
}; };
/* Delay for the loop waiting for READY frame (in ms) */ /* preamble and start bytes, see pn532-internal.h for details */
#define PN532_RDY_LOOP_DELAY 90 const uint8_t pn53x_preamble_and_start[] = { 0x00, 0x00, 0xff };
#define PN53X_PREAMBLE_AND_START_LEN (sizeof(pn53x_preamble_and_start) / sizeof(pn53x_preamble_and_start[0]))
const struct timespec rdyDelay = {
.tv_sec = 0,
.tv_nsec = PN532_RDY_LOOP_DELAY * 1000 * 1000
};
/* Private Functions Prototypes */ /* Private Functions Prototypes */
@ -96,6 +99,70 @@ static size_t pn532_i2c_scan(const nfc_context *context, nfc_connstring connstri
#define DRIVER_DATA(pnd) ((struct pn532_i2c_data*)(pnd->driver_data)) #define DRIVER_DATA(pnd) ((struct pn532_i2c_data*)(pnd->driver_data))
/*
* Bus free time (in ms) between a STOP condition and START condition. See
* tBuf in the PN532 data sheet, section 12.25: Timing for the I2C interface,
* table 320. I2C timing specification, page 211, rev. 3.2 - 2007-12-07.
*/
#define PN532_BUS_FREE_TIME 5
static struct timespec __transaction_stop;
/**
* @brief Wrapper around i2c_read to ensure proper timing by respecting the
* minimal free bus time between a STOP condition and a START condition.
*
* @note This is not thread safe, but since libnfc is single threaded
* this should be okay.
*
* @param id I2C device
* @param buf pointer on buffer used to store data
* @param len length of the buffer
* @return length (in bytes) of read data, or driver error code (negative value)
*/
static ssize_t pn532_i2c_read(const i2c_device id,
uint8_t *buf, const size_t len)
{
struct timespec transaction_start, bus_free_time = { 0 };
ssize_t ret;
clock_gettime(CLOCK_MONOTONIC, &transaction_start);
bus_free_time.tv_nsec = (PN532_BUS_FREE_TIME * 1000 * 1000) -
(transaction_start.tv_nsec - __transaction_stop.tv_nsec);
nanosleep(&bus_free_time, NULL);
ret = i2c_read(id, buf, len);
clock_gettime(CLOCK_MONOTONIC, &__transaction_stop);
return ret;
}
/**
* @brief Wrapper around i2c_write to ensure proper timing by respecting the
* minimal free bus time between a STOP condition and a START condition.
*
* @note This is not thread safe, but since libnfc is single threaded
* this should be okay.
*
* @param id I2C device
* @param buf pointer on buffer containing data
* @param len length of the buffer
* @return NFC_SUCCESS on success, otherwise driver error code
*/
static ssize_t pn532_i2c_write(const i2c_device id,
const uint8_t *buf, const size_t len)
{
struct timespec transaction_start, bus_free_time = { 0 };
ssize_t ret;
clock_gettime(CLOCK_MONOTONIC, &transaction_start);
bus_free_time.tv_nsec = (PN532_BUS_FREE_TIME * 1000 * 1000) -
(transaction_start.tv_nsec - __transaction_stop.tv_nsec);
nanosleep(&bus_free_time, NULL);
ret = i2c_write(id, buf, len);
clock_gettime(CLOCK_MONOTONIC, &__transaction_stop);
return ret;
}
/** /**
* @brief Scan all available I2C buses to find PN532 devices. * @brief Scan all available I2C buses to find PN532 devices.
* *
@ -308,6 +375,7 @@ static int
pn532_i2c_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int timeout) pn532_i2c_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int timeout)
{ {
int res = 0; int res = 0;
uint8_t retries;
// Discard any existing data ? // Discard any existing data ?
@ -334,15 +402,22 @@ pn532_i2c_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int
break; break;
}; };
uint8_t abtFrame[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff }; // Every packet must start with "00 00 ff" uint8_t abtFrame[PN532_BUFFER_LEN];
size_t szFrame = 0; size_t szFrame = 0;
memcpy(abtFrame, pn53x_preamble_and_start, PN53X_PREAMBLE_AND_START_LEN); // Every packet must start with the preamble and start bytes.
if ((res = pn53x_build_frame(abtFrame, &szFrame, pbtData, szData)) < 0) { if ((res = pn53x_build_frame(abtFrame, &szFrame, pbtData, szData)) < 0) {
pnd->last_error = res; pnd->last_error = res;
return pnd->last_error; return pnd->last_error;
} }
res = i2c_write(DRIVER_DATA(pnd)->dev, abtFrame, szFrame); for (retries = PN532_SEND_RETRIES; retries > 0; retries--) {
res = pn532_i2c_write(DRIVER_DATA(pnd)->dev, abtFrame, szFrame);
if (res >= 0)
break;
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Failed to transmit data. Retries left: %d.", retries - 1);
}
if (res < 0) { if (res < 0) {
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to transmit data. (TX)"); log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to transmit data. (TX)");
@ -400,10 +475,7 @@ pn532_i2c_wait_rdyframe(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLe
} }
do { do {
// Wait a little bit before reading int recCount = pn532_i2c_read(DRIVER_DATA(pnd)->dev, i2cRx, szDataLen + 1);
nanosleep(&rdyDelay, (struct timespec *) NULL);
int recCount = i2c_read(DRIVER_DATA(pnd)->dev, i2cRx, szDataLen + 1);
if (DRIVER_DATA(pnd)->abort_flag) { if (DRIVER_DATA(pnd)->abort_flag) {
// Reset abort flag // Reset abort flag
@ -477,9 +549,7 @@ pn532_i2c_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int
goto error; goto error;
} }
const uint8_t pn53x_preamble[3] = { 0x00, 0x00, 0xff }; if (0 != (memcmp(frameBuf, pn53x_preamble_and_start, PN53X_PREAMBLE_AND_START_LEN))) {
if (0 != (memcmp(frameBuf, pn53x_preamble, 3))) {
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame preamble+start code mismatch"); log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame preamble+start code mismatch");
pnd->last_error = NFC_EIO; pnd->last_error = NFC_EIO;
goto error; goto error;
@ -572,7 +642,7 @@ error:
int int
pn532_i2c_ack(nfc_device *pnd) pn532_i2c_ack(nfc_device *pnd)
{ {
return i2c_write(DRIVER_DATA(pnd)->dev, pn53x_ack_frame, sizeof(pn53x_ack_frame)); return pn532_i2c_write(DRIVER_DATA(pnd)->dev, pn53x_ack_frame, sizeof(pn53x_ack_frame));
} }
/** /**

View file

@ -39,6 +39,5 @@
uint8_t mirror(uint8_t bt); uint8_t mirror(uint8_t bt);
uint32_t mirror32(uint32_t ui32Bits); uint32_t mirror32(uint32_t ui32Bits);
uint64_t mirror64(uint64_t ui64Bits); uint64_t mirror64(uint64_t ui64Bits);
void mirror_uint8_ts(uint8_t *pbts, size_t szLen);
#endif // _LIBNFC_MIRROR_SUBR_H_ #endif // _LIBNFC_MIRROR_SUBR_H_

View file

@ -242,7 +242,6 @@ connstring_decode(const nfc_connstring connstring, const char *driver_name, cons
int res = sscanf(connstring, format, param0, param1, param2); int res = sscanf(connstring, format, param0, param1, param2);
if (res < 1 || ((0 != strcmp(param0, driver_name)) && if (res < 1 || ((0 != strcmp(param0, driver_name)) &&
(bus_name != NULL) &&
(0 != strcmp(param0, bus_name)))) { (0 != strcmp(param0, bus_name)))) {
// Driver name does not match. // Driver name does not match.
res = 0; res = 0;

View file

@ -645,7 +645,7 @@ nfc_initiator_poll_target(nfc_device *pnd,
* @param pnd \a nfc_device struct pointer that represent currently used device * @param pnd \a nfc_device struct pointer that represent currently used device
* @param ndm desired D.E.P. mode (\a NDM_ACTIVE or \a NDM_PASSIVE for active, respectively passive mode) * @param ndm desired D.E.P. mode (\a NDM_ACTIVE or \a NDM_PASSIVE for active, respectively passive mode)
* @param nbr desired baud rate * @param nbr desired baud rate
* @param ndiInitiator pointer \a nfc_dep_info struct that contains \e NFCID3 and \e General \e Bytes to set to the initiator device (optionnal, can be \e NULL) * @param pndiInitiator pointer \a nfc_dep_info struct that contains \e NFCID3 and \e General \e Bytes to set to the initiator device (optionnal, can be \e NULL)
* @param[out] pnt is a \a nfc_target struct pointer where target information will be put. * @param[out] pnt is a \a nfc_target struct pointer where target information will be put.
* @param timeout in milliseconds * @param timeout in milliseconds
* *
@ -673,7 +673,7 @@ nfc_initiator_select_dep_target(nfc_device *pnd,
* @param pnd \a nfc_device struct pointer that represent currently used device * @param pnd \a nfc_device struct pointer that represent currently used device
* @param ndm desired D.E.P. mode (\a NDM_ACTIVE or \a NDM_PASSIVE for active, respectively passive mode) * @param ndm desired D.E.P. mode (\a NDM_ACTIVE or \a NDM_PASSIVE for active, respectively passive mode)
* @param nbr desired baud rate * @param nbr desired baud rate
* @param ndiInitiator pointer \a nfc_dep_info struct that contains \e NFCID3 and \e General \e Bytes to set to the initiator device (optionnal, can be \e NULL) * @param pndiInitiator pointer \a nfc_dep_info struct that contains \e NFCID3 and \e General \e Bytes to set to the initiator device (optionnal, can be \e NULL)
* @param[out] pnt is a \a nfc_target struct pointer where target information will be put. * @param[out] pnt is a \a nfc_target struct pointer where target information will be put.
* @param timeout in milliseconds * @param timeout in milliseconds
* *
@ -972,7 +972,7 @@ nfc_target_init(nfc_device *pnd, nfc_target *pnt, uint8_t *pbtRx, const size_t s
* @param pnd \a nfc_device struct pointer that represent currently used device * @param pnd \a nfc_device struct pointer that represent currently used device
* *
* This function switch the device in idle mode. * This function switch the device in idle mode.
* In initiator mode, the RF field is turned off and the device is set to low power mode (if avaible); * In initiator mode, the RF field is turned off and the device is set to low power mode (if available);
* In target mode, the emulation is stoped (no target available from external initiator) and the device is set to low power mode (if avaible). * In target mode, the emulation is stoped (no target available from external initiator) and the device is set to low power mode (if avaible).
*/ */
int int

View file

@ -671,7 +671,7 @@ main(int argc, const char *argv[])
if ((nt.nti.nai.abtAtqa[1] & 0x02) == 0x02) if ((nt.nti.nai.abtAtqa[1] & 0x02) == 0x02)
// 4K // 4K
uiBlocks = 0xff; uiBlocks = 0xff;
else if ((nt.nti.nai.btSak & 0x01) == 0x01) else if (nt.nti.nai.btSak == 0x09)
// 320b // 320b
uiBlocks = 0x13; uiBlocks = 0x13;
else else

View file

@ -98,7 +98,7 @@ read_card(void)
{ {
uint32_t page; uint32_t page;
bool bFailure = false; bool bFailure = false;
uint32_t uiReadedPages = 0; uint32_t uiReadPages = 0;
printf("Reading %d pages |", uiBlocks + 1); printf("Reading %d pages |", uiBlocks + 1);
@ -111,13 +111,13 @@ read_card(void)
break; break;
} }
print_success_or_failure(bFailure, &uiReadedPages); print_success_or_failure(bFailure, &uiReadPages);
print_success_or_failure(bFailure, &uiReadedPages); print_success_or_failure(bFailure, &uiReadPages);
print_success_or_failure(bFailure, &uiReadedPages); print_success_or_failure(bFailure, &uiReadPages);
print_success_or_failure(bFailure, &uiReadedPages); print_success_or_failure(bFailure, &uiReadPages);
} }
printf("|\n"); printf("|\n");
printf("Done, %d of %d pages read.\n", uiReadedPages, uiBlocks + 1); printf("Done, %d of %d pages read.\n", uiReadPages, uiBlocks + 1);
fflush(stdout); fflush(stdout);
return (!bFailure); return (!bFailure);
@ -234,7 +234,7 @@ write_card(bool write_otp, bool write_lock, bool write_uid)
{ {
uint32_t uiBlock = 0; uint32_t uiBlock = 0;
bool bFailure = false; bool bFailure = false;
uint32_t uiWritenPages = 0; uint32_t uiWrittenPages = 0;
uint32_t uiSkippedPages = 0; uint32_t uiSkippedPages = 0;
char buffer[BUFSIZ]; char buffer[BUFSIZ];
@ -276,7 +276,7 @@ write_card(bool write_otp, bool write_lock, bool write_uid)
} }
} }
for (uint32_t page = uiSkippedPages; page <= ((uiBlocks / 4) * 4); page++) { for (uint32_t page = uiSkippedPages; page <= uiBlocks; page++) {
if ((page == 0x2) && (!write_lock)) { if ((page == 0x2) && (!write_lock)) {
printf("s"); printf("s");
uiSkippedPages++; uiSkippedPages++;
@ -306,10 +306,10 @@ write_card(bool write_otp, bool write_lock, bool write_uid)
if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, page, &mp)) if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, page, &mp))
bFailure = true; bFailure = true;
print_success_or_failure(bFailure, &uiWritenPages); print_success_or_failure(bFailure, &uiWrittenPages);
} }
printf("|\n"); printf("|\n");
printf("Done, %d of %d pages written (%d pages skipped).\n", uiWritenPages, uiBlocks + 1, uiSkippedPages); printf("Done, %d of %d pages written (%d pages skipped).\n", uiWrittenPages, uiBlocks + 1, uiSkippedPages);
return true; return true;
} }