diff --git a/README.md b/README.md index 1f3f8e72..e86302b6 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,17 @@ file per device in a nfc/devices.d directory: printf 'name = "My first device"\nconnstring = "pn532_uart:/dev/ttyACM0"\n' | sudo tee /etc/nfc/devices.d/first.conf printf 'name = "My second device"\nconnstring = "pn532_uart:/dev/ttyACM1"\n' | sudo tee /etc/nfc/devices.d/second.conf +Environment Variables +===================== +You can override certain configuration options at runtime using the following environment variables: ++ `LIBNFC_DEFAULT_DEVICE=`: `LIBNFC_DEFAULT_DEVICE=pn532_uart:/dev/ttyACM0` will use pn532 on /dev/ttyACM0 as default device ++ `LIBNFC_DEVICE=` will ignore all devices in the config files and use only the one defined in the variable ++ `LIBNFC_AUTO_SCAN=` overrides `allow_autoscan` option in the config file ++ `LIBNFC_INTRUSIVE_SCAN=` overrides `allow_intrusive_scan` option in the config file ++ `LIBNFC_LOG_LEVEL=<0|1|2|3>` overrides `log_level` option in the config file + +To obtain the connstring of a recognized device, you can use `nfc-scan-device`: `LIBNFC_AUTO_SCAN=true nfc-scan-device` will show the names & connstrings of all found devices. + How to report bugs ================== diff --git a/examples/nfc-st25tb.c b/examples/nfc-st25tb.c index bcca55b1..7eb5deeb 100644 --- a/examples/nfc-st25tb.c +++ b/examples/nfc-st25tb.c @@ -50,7 +50,7 @@ * * Tested with * - ST25TB512-AC - (BE/Brussels/STIB ; AliExpress ones) - * - ST25TB512-AT - (FR/Lille/Ilevia ; FR/Reims/Citura) + * - ST25TB512-AT - (FR/Lille/Ilevia ; FR/Reims/Citura ; FR/Dijon/Divia ; FR/Strasbourg/CTS) * - SRT512 - legacy - (FR/Bordeaux/TBM) * - SRI512 - legacy - (anonymous vending machine) */ @@ -94,7 +94,7 @@ int main(int argc, char *argv[]) nfc_context *context = NULL; nfc_device *pnd = NULL; nfc_target nt = {0}; - nfc_modulation nm = {NMT_ISO14443B, NBR_106}; + nfc_modulation nm = {NMT_ISO14443B2SR, NBR_106}; const st_data * stcurrent; int opt, res; bool bIsBlock = false, bIsRead = false, bIsWrite = false, bIsBadCli = false; @@ -205,45 +205,35 @@ int main(int argc, char *argv[]) { printf("Reader : %s - via %s\n ...wait for card...\n", nfc_device_get_name(pnd), nfc_device_get_connstring(pnd)); - res = nfc_initiator_list_passive_targets(pnd, nm, &nt, 1); - if(res == 0) // we don't really wanted a NMT_ISO14443B + if (nfc_initiator_select_passive_target(pnd, nm, NULL, 0, &nt) > 0) { - nm.nmt = NMT_ISO14443B2SR; // we want a NMT_ISO14443B2SR, but needed to ask for NMT_ISO14443B before - if (nfc_initiator_select_passive_target(pnd, nm, NULL, 0, &nt) > 0) + stcurrent = get_info(&nt, true); + if(stcurrent) { - stcurrent = get_info(&nt, true); - if(stcurrent) - { - printf("\n"); + printf("\n"); - if(bIsBlock && (bIsRead || bIsWrite)) + if(bIsBlock && (bIsRead || bIsWrite)) + { + if(bIsRead) { - if(bIsRead) - { - get_block_at(pnd, blockNumber, NULL, 0, true); - } - - if(bIsWrite) - { - set_block_at_confirmed(pnd, blockNumber, data, cbData, true); - } + get_block_at(pnd, blockNumber, NULL, 0, true); } - else if(!bIsRead && !bIsWrite && !bIsBlock) + + if(bIsWrite) + { + set_block_at_confirmed(pnd, blockNumber, data, cbData, true); + } + } + else if(!bIsRead && !bIsWrite && !bIsBlock) + { + for(i = 0; i < stcurrent->nbNormalBlock; i++) { - for(i = 0; i < stcurrent->nbNormalBlock; i++) - { - get_block_at(pnd, i, NULL, 0, true); - } - display_system_info(pnd, stcurrent); + get_block_at(pnd, i, NULL, 0, true); } + display_system_info(pnd, stcurrent); } } } - else if(res > 0) - { - printf("ERROR - We got a NMT_ISO14443B ?\n"); - } - else printf("ERROR - nfc_initiator_list_passive_targets: %i\n", res); } else printf("ERROR - nfc_initiator_init: %i\n", res); @@ -606,4 +596,5 @@ void print_hex(const uint8_t *pbtData, const size_t szBytes) { printf("%02hhx ", pbtData[szPos]); } -} \ No newline at end of file +} + diff --git a/libnfc/buses/usbbus.c b/libnfc/buses/usbbus.c index b1979a6a..d5a12478 100644 --- a/libnfc/buses/usbbus.c +++ b/libnfc/buses/usbbus.c @@ -39,301 +39,308 @@ static libusb_context *ctx = NULL; uint8_t get_usb_num_configs(struct libusb_device *dev); -int usbbus_prepare() { - static bool usb_initialized = false; - int res; - if (!usb_initialized) { +int usbbus_prepare() +{ + static bool usb_initialized = false; + int res; + if (!usb_initialized) { #ifdef ENVVARS - char *env_log_level = getenv("LIBNFC_LOG_LEVEL"); - // Set libusb debug only if asked explicitely: - // LIBUSB_LOG_LEVEL=12288 (= NFC_LOG_PRIORITY_DEBUG * 2 ^ NFC_LOG_GROUP_LIBUSB) - if (env_log_level - && (((atoi(env_log_level) >> (NFC_LOG_GROUP_LIBUSB * 2)) & 0x00000003) >= NFC_LOG_PRIORITY_DEBUG)) { - setenv("USB_DEBUG", "255", 1); - } + char *env_log_level = getenv("LIBNFC_LOG_LEVEL"); + // Set libusb debug only if asked explicitely: + // LIBUSB_LOG_LEVEL=12288 (= NFC_LOG_PRIORITY_DEBUG * 2 ^ NFC_LOG_GROUP_LIBUSB) + if (env_log_level + && (((atoi(env_log_level) >> (NFC_LOG_GROUP_LIBUSB * 2)) & 0x00000003) >= NFC_LOG_PRIORITY_DEBUG)) { + setenv("USB_DEBUG", "255", 1); + } #endif - res = libusb_init(&ctx); - if (res != 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to init libusb (%s)", - libusb_strerror(res)); - return res; - } - usb_initialized = true; - } - - // usb_find_devices will find all of the devices on each bus. This should be - // called after usb_find_busses. Returns the number of changes since the - // previous call to this function (total of new device and devices removed). - libusb_device **tmp_devices; - ssize_t num_devices = libusb_get_device_list(ctx, &tmp_devices); - libusb_free_device_list(tmp_devices, (int) num_devices); - if (num_devices <= 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to find USB devices (%s)", - libusb_strerror((int) num_devices)); - return -1; - } - return 0; + res = libusb_init(&ctx); + if (res != 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to init libusb (%s)", + libusb_strerror(res)); + return res; + } + usb_initialized = true; + } + + // usb_find_devices will find all of the devices on each bus. This should be + // called after usb_find_busses. Returns the number of changes since the + // previous call to this function (total of new device and devices removed). + libusb_device **tmp_devices; + ssize_t num_devices = libusb_get_device_list(ctx, &tmp_devices); + libusb_free_device_list(tmp_devices, (int) num_devices); + if (num_devices <= 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to find USB devices (%s)", + libusb_strerror((int) num_devices)); + return -1; + } + return 0; } -//TODO kenspeckle -//beim ende vom programm libusb dinge wieder freigeben - size_t usbbus_usb_scan(nfc_connstring connstrings[], const size_t connstrings_len, struct usbbus_device *nfc_usb_devices, const size_t num_nfc_usb_devices, - char *usb_driver_name) { - usbbus_prepare(); - - size_t device_found = 0; - struct libusb_device **devices; - ssize_t num_devices = libusb_get_device_list(ctx, &devices); - for (size_t i = 0; i < num_devices; i++) { - struct libusb_device *dev = devices[i]; - - for (size_t nfc_dev_idx = 0; nfc_dev_idx < num_nfc_usb_devices; nfc_dev_idx++) { - if (nfc_usb_devices[nfc_dev_idx].vendor_id == usbbus_get_vendor_id(dev) - && nfc_usb_devices[nfc_dev_idx].product_id == usbbus_get_product_id(dev)) { - - size_t valid_config_idx = 1; - - // Make sure there are 2 endpoints available - // with libusb-win32 we got some null pointers so be robust before looking at endpoints - if (nfc_usb_devices[nfc_dev_idx].max_packet_size == 0) { - - bool found_valid_config = false; - - for (size_t config_idx = 0; config_idx < get_usb_num_configs(dev); i++) { - struct libusb_config_descriptor *usb_config; - int r = libusb_get_config_descriptor(dev, config_idx, &usb_config); - - if (r != 0 - || usb_config->interface == NULL - || usb_config->interface->altsetting == NULL - || usb_config->interface->altsetting->bNumEndpoints < 2) { - // Nope, we maybe want the next one, let's try to find another - libusb_free_config_descriptor(usb_config); - continue; - } - - libusb_free_config_descriptor(usb_config); - - found_valid_config = true; - valid_config_idx = config_idx; - break; - } - if (!found_valid_config) { - libusb_unref_device(dev); - continue; - } - } - - libusb_device_handle *udev; - int res = libusb_open(dev, &udev); - if (res < 0 && udev == NULL) { - libusb_unref_device(dev); - continue; - } - - // Set configuration - res = libusb_set_configuration(udev, (int) valid_config_idx); - if (res < 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to set USB configuration (%s)", - libusb_strerror(res)); - libusb_close(udev); - libusb_unref_device(dev); - // we failed to use the device - continue; - } - - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "device found: Vendor-Id: %d Product-Id %d", - usbbus_get_vendor_id(dev), usbbus_get_product_id(dev)); - libusb_close(udev); - - uint8_t dev_address = libusb_get_device_address(dev); - printf("%s:%03d:%03d", - "Test", - dev_address, - (int) valid_config_idx); - size_t size_new_str = snprintf( - connstrings[device_found], - sizeof(connstrings[device_found]), - "%s:%03d:%03d", - usb_driver_name, - dev_address, - (int) valid_config_idx); - - if (size_new_str >= (int) sizeof(nfc_connstring)) { - // truncation occurred, skipping that one - libusb_unref_device(dev); - continue; - } - device_found++; - // Test if we reach the maximum "wanted" devices - if (device_found == connstrings_len) { - libusb_free_device_list(devices, 0); - return device_found; - } - } - } - } - libusb_free_device_list(devices, 0); - return device_found; + char *usb_driver_name) +{ + usbbus_prepare(); + + size_t device_found = 0; + struct libusb_device **devices; + ssize_t num_devices = libusb_get_device_list(ctx, &devices); + for (size_t i = 0; i < num_devices; i++) { + struct libusb_device *dev = devices[i]; + + for (size_t nfc_dev_idx = 0; nfc_dev_idx < num_nfc_usb_devices; nfc_dev_idx++) { + if (nfc_usb_devices[nfc_dev_idx].vendor_id == usbbus_get_vendor_id(dev) + && nfc_usb_devices[nfc_dev_idx].product_id == usbbus_get_product_id(dev)) { + + size_t valid_config_idx = 1; + + // Make sure there are 2 endpoints available + // with libusb-win32 we got some null pointers so be robust before looking at endpoints + if (nfc_usb_devices[nfc_dev_idx].max_packet_size == 0) { + + bool found_valid_config = false; + + for (size_t config_idx = 0; config_idx < get_usb_num_configs(dev); i++) { + struct libusb_config_descriptor *usb_config; + int r = libusb_get_config_descriptor(dev, config_idx, &usb_config); + + if (r != 0 + || usb_config->interface == NULL + || usb_config->interface->altsetting == NULL + || usb_config->interface->altsetting->bNumEndpoints < 2) { + // Nope, we maybe want the next one, let's try to find another + libusb_free_config_descriptor(usb_config); + continue; + } + + libusb_free_config_descriptor(usb_config); + + found_valid_config = true; + valid_config_idx = config_idx; + break; + } + if (!found_valid_config) { + libusb_unref_device(dev); + continue; + } + } + + libusb_device_handle *udev; + int res = libusb_open(dev, &udev); + if (res < 0 && udev == NULL) { + libusb_unref_device(dev); + continue; + } + + // Set configuration + res = libusb_set_configuration(udev, (int) valid_config_idx); + if (res < 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to set USB configuration (%s)", + libusb_strerror(res)); + libusb_close(udev); + libusb_unref_device(dev); + // we failed to use the device + continue; + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "device found: Vendor-Id: %d Product-Id %d", + usbbus_get_vendor_id(dev), usbbus_get_product_id(dev)); + libusb_close(udev); + + uint8_t dev_address = libusb_get_device_address(dev); + printf("%s:%03d:%03d", + "Test", + dev_address, + (int) valid_config_idx); + size_t size_new_str = snprintf( + connstrings[device_found], + sizeof(connstrings[device_found]), + "%s:%03d:%03d", + usb_driver_name, + dev_address, + (int) valid_config_idx); + + if (size_new_str >= (int) sizeof(nfc_connstring)) { + // truncation occurred, skipping that one + libusb_unref_device(dev); + continue; + } + device_found++; + // Test if we reach the maximum "wanted" devices + if (device_found == connstrings_len) { + libusb_free_device_list(devices, 0); + return device_found; + } + } + } + } + libusb_free_device_list(devices, 0); + return device_found; } void usbbus_get_usb_endpoints(struct libusb_device *dev, uint8_t *endpoint_in, uint8_t *endpoint_out, - uint16_t *max_packet_size) { - - bool endpoint_in_set = false; - bool endpoint_out_set = false; - size_t num_configs = get_usb_num_configs(dev); - for (size_t config_idx = 0; config_idx < num_configs; config_idx++) { - struct libusb_config_descriptor *usb_config; - - int r = libusb_get_config_descriptor(dev, config_idx, &usb_config); - if (r != 0) { - continue; - } - - if (!usb_config->interface) { - continue; - } - for (size_t interface_idx = 0; interface_idx < usb_config->bNumInterfaces; interface_idx++) { - struct libusb_interface interface = usb_config->interface[interface_idx]; - if (!interface.altsetting) { - continue; - } - for (size_t settings_idx = 0; settings_idx < interface.num_altsetting; settings_idx++) { - struct libusb_interface_descriptor settings = interface.altsetting[settings_idx]; - if (!settings.endpoint) { - continue; - } - - // 3 Endpoints maximum: Interrupt In, Bulk In, Bulk Out - for (size_t endpoint_idx = 0; endpoint_idx < settings.bNumEndpoints; endpoint_idx++) { - struct libusb_endpoint_descriptor endpoint = settings.endpoint[endpoint_idx]; - - // Only accept bulk transfer endpoints (ignore interrupt endpoints) - if (endpoint.bmAttributes != LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK) { - continue; - } - - // Copy the endpoint to a local var, makes it more readable code - uint8_t endpoint_address = endpoint.bEndpointAddress; - - // Test if we dealing with a bulk IN endpoint - if ((endpoint_address & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN && !endpoint_in_set) { - *endpoint_in = endpoint_address; - *max_packet_size = endpoint.wMaxPacketSize; - endpoint_in_set = true; - } - // Test if we dealing with a bulk OUT endpoint - if ((endpoint_address & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT && !endpoint_out_set) { - *endpoint_out = endpoint_address; - *max_packet_size = endpoint.wMaxPacketSize; - endpoint_out_set = true; - } - - if (endpoint_in_set && endpoint_out_set) { - libusb_free_config_descriptor(usb_config); - return; - } - } - } - } - - libusb_free_config_descriptor(usb_config); - } + uint16_t *max_packet_size) +{ + + bool endpoint_in_set = false; + bool endpoint_out_set = false; + size_t num_configs = get_usb_num_configs(dev); + for (size_t config_idx = 0; config_idx < num_configs; config_idx++) { + struct libusb_config_descriptor *usb_config; + + int r = libusb_get_config_descriptor(dev, config_idx, &usb_config); + if (r != 0) { + continue; + } + + if (!usb_config->interface) { + continue; + } + for (size_t interface_idx = 0; interface_idx < usb_config->bNumInterfaces; interface_idx++) { + struct libusb_interface interface = usb_config->interface[interface_idx]; + if (!interface.altsetting) { + continue; + } + for (size_t settings_idx = 0; settings_idx < interface.num_altsetting; settings_idx++) { + struct libusb_interface_descriptor settings = interface.altsetting[settings_idx]; + if (!settings.endpoint) { + continue; + } + + // 3 Endpoints maximum: Interrupt In, Bulk In, Bulk Out + for (size_t endpoint_idx = 0; endpoint_idx < settings.bNumEndpoints; endpoint_idx++) { + struct libusb_endpoint_descriptor endpoint = settings.endpoint[endpoint_idx]; + + // Only accept bulk transfer endpoints (ignore interrupt endpoints) + if (endpoint.bmAttributes != LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK) { + continue; + } + + // Copy the endpoint to a local var, makes it more readable code + uint8_t endpoint_address = endpoint.bEndpointAddress; + + // Test if we dealing with a bulk IN endpoint + if ((endpoint_address & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN && !endpoint_in_set) { + *endpoint_in = endpoint_address; + *max_packet_size = endpoint.wMaxPacketSize; + endpoint_in_set = true; + } + // Test if we dealing with a bulk OUT endpoint + if ((endpoint_address & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT && !endpoint_out_set) { + *endpoint_out = endpoint_address; + *max_packet_size = endpoint.wMaxPacketSize; + endpoint_out_set = true; + } + + if (endpoint_in_set && endpoint_out_set) { + libusb_free_config_descriptor(usb_config); + return; + } + } + } + } + + libusb_free_config_descriptor(usb_config); + } } -uint8_t get_usb_num_configs(struct libusb_device *dev) { - struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(dev, &descriptor); - return descriptor.bNumConfigurations; +uint8_t get_usb_num_configs(struct libusb_device *dev) +{ + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + return descriptor.bNumConfigurations; } -void usbbus_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len) { - struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(dev, &descriptor); - if (descriptor.iManufacturer || descriptor.iProduct) { - if (udev) { - libusb_get_string_descriptor_ascii(udev, descriptor.iManufacturer & 0xff, (unsigned char *) buffer, len); - if (strlen(buffer) > 0) { - strncpy(buffer + strlen(buffer), " / ", 4); - } - libusb_get_string_descriptor_ascii(udev, - descriptor.iProduct & 0xff, - (unsigned char *) buffer + strlen(buffer), - len - strlen(buffer)); - } - } +void usbbus_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len) +{ + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + if (descriptor.iManufacturer || descriptor.iProduct) { + if (udev) { + libusb_get_string_descriptor_ascii(udev, descriptor.iManufacturer & 0xff, (unsigned char *) buffer, len); + if (strlen(buffer) > 0) { + strncpy(buffer + strlen(buffer), " / ", 4); + } + libusb_get_string_descriptor_ascii(udev, + descriptor.iProduct & 0xff, + (unsigned char *) buffer + strlen(buffer), + len - strlen(buffer)); + } + } } -void usbbus_get_device(uint8_t dev_address, struct libusb_device ** dev, struct libusb_device_handle ** dev_handle) { - struct libusb_device ** device_list; - ssize_t num_devices = libusb_get_device_list(ctx, &device_list); - for (size_t i = 0; i < num_devices; i++) { - if (libusb_get_device_address(device_list[i]) != dev_address) { - continue; - } else { - *dev = device_list[i]; - int res = libusb_open(*dev, dev_handle); - if (res != 0 || dev_handle == NULL) { - log_put(LOG_GROUP,LOG_CATEGORY,NFC_LOG_PRIORITY_ERROR, - "Unable to open libusb device (%s)",libusb_strerror(res)); - continue; - } - } - } - - // libusb works with a reference counter which is set to 1 for each device when calling libusb_get_device_list and increased - // by libusb_open. Thus we decrease the counter by 1 for all devices and only the "real" device will survive - libusb_free_device_list(device_list, num_devices); +void usbbus_get_device(uint8_t dev_address, struct libusb_device **dev, struct libusb_device_handle **dev_handle) +{ + struct libusb_device **device_list; + ssize_t num_devices = libusb_get_device_list(ctx, &device_list); + for (size_t i = 0; i < num_devices; i++) { + if (libusb_get_device_address(device_list[i]) != dev_address) { + continue; + } else { + *dev = device_list[i]; + int res = libusb_open(*dev, dev_handle); + if (res != 0 || dev_handle == NULL) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, + "Unable to open libusb device (%s)", libusb_strerror(res)); + continue; + } + } + } + + // libusb works with a reference counter which is set to 1 for each device when calling libusb_get_device_list and increased + // by libusb_open. Thus we decrease the counter by 1 for all devices and only the "real" device will survive + libusb_free_device_list(device_list, num_devices); } -void usbbus_close(struct libusb_device * dev, struct libusb_device_handle * dev_handle) { - libusb_close(dev_handle); - libusb_unref_device(dev); - libusb_exit(ctx); +void usbbus_close(struct libusb_device *dev, struct libusb_device_handle *dev_handle) +{ + libusb_close(dev_handle); + libusb_unref_device(dev); + libusb_exit(ctx); } -uint16_t usbbus_get_vendor_id(struct libusb_device *dev) { - struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(dev, &descriptor); - return descriptor.idVendor; +uint16_t usbbus_get_vendor_id(struct libusb_device *dev) +{ + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + return descriptor.idVendor; } -uint16_t usbbus_get_product_id(struct libusb_device *dev) { - struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(dev, &descriptor); - return descriptor.idProduct; +uint16_t usbbus_get_product_id(struct libusb_device *dev) +{ + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + return descriptor.idProduct; } -int usbbus_get_num_alternate_settings(struct libusb_device *dev, uint8_t config_idx) { - struct libusb_config_descriptor *usb_config; - int r = libusb_get_config_descriptor(dev, config_idx, &usb_config); - if (r != 0 || usb_config == NULL) { - return -1; - } - libusb_free_config_descriptor(usb_config); - return usb_config->interface->num_altsetting; +int usbbus_get_num_alternate_settings(struct libusb_device *dev, uint8_t config_idx) +{ + struct libusb_config_descriptor *usb_config; + int r = libusb_get_config_descriptor(dev, config_idx, &usb_config); + if (r != 0 || usb_config == NULL) { + return -1; + } + libusb_free_config_descriptor(usb_config); + return usb_config->interface->num_altsetting; } diff --git a/libnfc/buses/usbbus.h b/libnfc/buses/usbbus.h index ba18c3bb..3caceddb 100644 --- a/libnfc/buses/usbbus.h +++ b/libnfc/buses/usbbus.h @@ -36,22 +36,22 @@ #define EMPTY_STRING ((unsigned char *)"\0") struct usbbus_device { - uint16_t vendor_id; - uint16_t product_id; - const char *name; - uint16_t max_packet_size; + uint16_t vendor_id; + uint16_t product_id; + const char *name; + uint16_t max_packet_size; }; int usbbus_prepare(); -size_t usbbus_usb_scan(nfc_connstring connstrings[], size_t connstrings_len, struct usbbus_device * nfc_usb_devices, size_t num_nfc_usb_devices, char * usb_driver_name); -void usbbus_get_usb_endpoints(struct libusb_device *dev, uint8_t * endpoint_in, uint8_t * endpoint_out, uint16_t * max_packet_size); -void usbbus_get_usb_device_name(struct libusb_device * dev, libusb_device_handle *udev, char *buffer, size_t len); -void usbbus_get_device(uint8_t dev_address, struct libusb_device ** dev, struct libusb_device_handle ** dev_handle); -void usbbus_close(struct libusb_device * dev, struct libusb_device_handle * dev_handle); -uint16_t usbbus_get_vendor_id(struct libusb_device * dev); -uint16_t usbbus_get_product_id(struct libusb_device * dev); +size_t usbbus_usb_scan(nfc_connstring connstrings[], size_t connstrings_len, struct usbbus_device *nfc_usb_devices, size_t num_nfc_usb_devices, char *usb_driver_name); +void usbbus_get_usb_endpoints(struct libusb_device *dev, uint8_t *endpoint_in, uint8_t *endpoint_out, uint16_t *max_packet_size); +void usbbus_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len); +void usbbus_get_device(uint8_t dev_address, struct libusb_device **dev, struct libusb_device_handle **dev_handle); +void usbbus_close(struct libusb_device *dev, struct libusb_device_handle *dev_handle); +uint16_t usbbus_get_vendor_id(struct libusb_device *dev); +uint16_t usbbus_get_product_id(struct libusb_device *dev); int usbbus_get_num_alternate_settings(struct libusb_device *dev, uint8_t config_idx); #endif diff --git a/libnfc/chips/pn53x.c b/libnfc/chips/pn53x.c index d2b93961..e5d9d940 100644 --- a/libnfc/chips/pn53x.c +++ b/libnfc/chips/pn53x.c @@ -1155,6 +1155,14 @@ pn53x_initiator_select_passive_target_ext(struct nfc_device *pnd, uint8_t abtRx[1]; uint8_t *pbtInitData = (uint8_t *) "\x0b"; size_t szInitData = 1; + + if ((res = pn53x_write_register(pnd, PN53X_REG_CIU_TxAuto, 0xef, 0x07)) < 0) // Initial RFOn, Tx2 RFAutoEn, Tx1 RFAutoEn + return res; + if ((res = pn53x_write_register(pnd, PN53X_REG_CIU_CWGsP, 0x3f, 0x3f)) < 0) // Conductance of the P-Driver + return res; + if ((res = pn53x_write_register(pnd, PN53X_REG_CIU_ModGsP, 0x3f, 0x12)) < 0) // Driver P-output conductance for the time of modulation + return res; + // Getting random Chip_ID if ((res = pn53x_initiator_transceive_bytes(pnd, abtInitiate, szInitiateLen, abtRx, sizeof(abtRx), timeout)) < 0) { if ((res == NFC_ERFTRANS) && (CHIP_DATA(pnd)->last_status_byte == 0x01)) { // Chip timeout diff --git a/libnfc/drivers/acr122_usb.c b/libnfc/drivers/acr122_usb.c index b3ecb07e..70f38a11 100644 --- a/libnfc/drivers/acr122_usb.c +++ b/libnfc/drivers/acr122_usb.c @@ -145,48 +145,48 @@ PC_to_RDR_XfrBlock APDU #pragma pack(1) struct ccid_header { - uint8_t bMessageType; - uint32_t dwLength; - uint8_t bSlot; - uint8_t bSeq; - uint8_t bMessageSpecific[3]; + uint8_t bMessageType; + uint32_t dwLength; + uint8_t bSlot; + uint8_t bSeq; + uint8_t bMessageSpecific[3]; }; struct apdu_header { - uint8_t bClass; - uint8_t bIns; - uint8_t bP1; - uint8_t bP2; - uint8_t bLen; + uint8_t bClass; + uint8_t bIns; + uint8_t bP1; + uint8_t bP2; + uint8_t bLen; }; struct acr122_usb_tama_frame { - struct ccid_header ccid_header; - struct apdu_header apdu_header; - uint8_t tama_header; - uint8_t tama_payload[254 - ]; // According to ACR122U manual: Pseudo APDUs (Section 6.0), Lc is 1-byte long (Data In: 255-bytes). + struct ccid_header ccid_header; + struct apdu_header apdu_header; + uint8_t tama_header; + uint8_t tama_payload[254 + ]; // According to ACR122U manual: Pseudo APDUs (Section 6.0), Lc is 1-byte long (Data In: 255-bytes). }; struct acr122_usb_apdu_frame { - struct ccid_header ccid_header; - struct apdu_header apdu_header; - uint8_t apdu_payload[255]; // APDU Lc is 1-byte long + struct ccid_header ccid_header; + struct apdu_header apdu_header; + uint8_t apdu_payload[255]; // APDU Lc is 1-byte long }; #pragma pack() // Internal data struct struct acr122_usb_data { - libusb_device * dev; - libusb_device_handle *pudh; - uint8_t configIdx; - uint8_t uiEndPointIn; - uint8_t uiEndPointOut; - uint16_t uiMaxPacketSize; - volatile bool abort_flag; - // Keep some buffers to reduce memcpy() usage - struct acr122_usb_tama_frame tama_frame; - struct acr122_usb_apdu_frame apdu_frame; + libusb_device *dev; + libusb_device_handle *pudh; + uint8_t configIdx; + uint8_t uiEndPointIn; + uint8_t uiEndPointOut; + uint16_t uiMaxPacketSize; + volatile bool abort_flag; + // Keep some buffers to reduce memcpy() usage + struct acr122_usb_tama_frame tama_frame; + struct acr122_usb_apdu_frame apdu_frame; }; // CCID Bulk-Out messages type @@ -203,9 +203,9 @@ struct acr122_usb_data { // This frame template is copied at init time // Its designed for TAMA sending but is also used for simple ADPU frame: acr122_build_frame_from_apdu() will overwrite needed bytes const uint8_t acr122_usb_frame_template[] = { - PC_to_RDR_XfrBlock, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CCID header - 0xff, 0x00, 0x00, 0x00, 0x00, // ADPU header - 0xd4, // PN532 direction + PC_to_RDR_XfrBlock, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CCID header + 0xff, 0x00, 0x00, 0x00, 0x00, // ADPU header + 0xd4, // PN532 direction }; // APDUs instructions @@ -228,238 +228,244 @@ static int acr122_usb_send_apdu(nfc_device *pnd, const size_t out_size); static int -acr122_usb_bulk_read(struct acr122_usb_data *data, uint8_t abtRx[], const size_t szRx, const int timeout) { - int actual_length; - int res = libusb_bulk_transfer(data->pudh, data->uiEndPointIn, (unsigned char *) abtRx, szRx, &actual_length, timeout); - if (res == 0) { - LOG_HEX(NFC_LOG_GROUP_COM, "RX", abtRx, actual_length); - res = actual_length; - } else { - if (res != LIBUSB_ERROR_TIMEOUT) { - res = NFC_EIO; - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to read from USB (%s)", - libusb_strerror(res)); - } else { - res = NFC_ETIMEOUT; - } - } - return res; +acr122_usb_bulk_read(struct acr122_usb_data *data, uint8_t abtRx[], const size_t szRx, const int timeout) +{ + int actual_length; + int res = libusb_bulk_transfer(data->pudh, data->uiEndPointIn, (unsigned char *) abtRx, szRx, &actual_length, timeout); + if (res == 0) { + LOG_HEX(NFC_LOG_GROUP_COM, "RX", abtRx, actual_length); + res = actual_length; + } else { + if (res != LIBUSB_ERROR_TIMEOUT) { + res = NFC_EIO; + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to read from USB (%s)", + libusb_strerror(res)); + } else { + res = NFC_ETIMEOUT; + } + } + return res; } static int -acr122_usb_bulk_write(struct acr122_usb_data *data, uint8_t abtTx[], const size_t szTx, const int timeout) { - LOG_HEX(NFC_LOG_GROUP_COM, "TX", abtTx, szTx); - int actual_length; - int res = libusb_bulk_transfer(data->pudh, data->uiEndPointOut, (unsigned char *) abtTx, szTx, &actual_length, timeout); - if (res == 0) { - // HACK This little hack is a well know problem of USB, see http://www.libusb.org/ticket/6 for more details - if ((actual_length > 0) && ((actual_length % data->uiMaxPacketSize) == 0)) { - libusb_bulk_transfer(data->pudh, data->uiEndPointOut, EMPTY_STRING, 0, &actual_length, timeout); - } - } else { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to write to USB (%s)", libusb_strerror(res)); - if (res == LIBUSB_ERROR_TIMEOUT) { - res = NFC_ETIMEOUT; - } else { - res = NFC_EIO; - } - } - return res; +acr122_usb_bulk_write(struct acr122_usb_data *data, uint8_t abtTx[], const size_t szTx, const int timeout) +{ + LOG_HEX(NFC_LOG_GROUP_COM, "TX", abtTx, szTx); + int actual_length; + int res = libusb_bulk_transfer(data->pudh, data->uiEndPointOut, (unsigned char *) abtTx, szTx, &actual_length, timeout); + if (res == 0) { + // HACK This little hack is a well know problem of USB, see http://www.libusb.org/ticket/6 for more details + if ((actual_length > 0) && ((actual_length % data->uiMaxPacketSize) == 0)) { + libusb_bulk_transfer(data->pudh, data->uiEndPointOut, EMPTY_STRING, 0, &actual_length, timeout); + } + } else { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to write to USB (%s)", libusb_strerror(res)); + if (res == LIBUSB_ERROR_TIMEOUT) { + res = NFC_ETIMEOUT; + } else { + res = NFC_EIO; + } + } + return res; } struct acr122_usb_supported_device { - uint16_t vendor_id; - uint16_t product_id; - const char *name; - uint16_t max_packet_size; + uint16_t vendor_id; + uint16_t product_id; + const char *name; + uint16_t max_packet_size; }; const struct acr122_usb_supported_device acr122_usb_supported_devices[] = { - //TODO find real max_packet_sizes - {0x072F, 0x2200, "ACS ACR122", 0x40}, - {0x072F, 0x90CC, "Touchatag", 0x40}, - {0x072F, 0x2214, "ACS ACR1222", 0x40}, + //TODO find real max_packet_sizes + {0x072F, 0x2200, "ACS ACR122", 0x40}, + {0x072F, 0x90CC, "Touchatag", 0x40}, + {0x072F, 0x2214, "ACS ACR1222", 0x40}, }; const size_t - num_acr122_usb_supported_device = sizeof(acr122_usb_supported_devices) / sizeof(struct acr122_usb_supported_device); +num_acr122_usb_supported_device = sizeof(acr122_usb_supported_devices) / sizeof(struct acr122_usb_supported_device); static size_t -acr122_usb_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) { - (void) context; - struct usbbus_device devices[num_acr122_usb_supported_device]; - for (size_t i = 0; i < num_acr122_usb_supported_device; i++) { - devices[i].product_id = acr122_usb_supported_devices[i].product_id; - devices[i].vendor_id = acr122_usb_supported_devices[i].vendor_id; - devices[i].name = acr122_usb_supported_devices[i].name; - devices[i].max_packet_size = acr122_usb_supported_devices[i].max_packet_size; - } - return usbbus_usb_scan(connstrings, connstrings_len, devices, num_acr122_usb_supported_device, ACR122_USB_DRIVER_NAME); +acr122_usb_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) +{ + (void) context; + struct usbbus_device devices[num_acr122_usb_supported_device]; + for (size_t i = 0; i < num_acr122_usb_supported_device; i++) { + devices[i].product_id = acr122_usb_supported_devices[i].product_id; + devices[i].vendor_id = acr122_usb_supported_devices[i].vendor_id; + devices[i].name = acr122_usb_supported_devices[i].name; + devices[i].max_packet_size = acr122_usb_supported_devices[i].max_packet_size; + } + return usbbus_usb_scan(connstrings, connstrings_len, devices, num_acr122_usb_supported_device, ACR122_USB_DRIVER_NAME); } static bool -acr122_usb_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len) { - *buffer = '\0'; - - usbbus_get_usb_device_name(dev, udev, buffer, len); - uint16_t vendor_id = usbbus_get_vendor_id(dev); - uint16_t product_id = usbbus_get_product_id(dev); - - if (!*buffer) { - for (size_t n = 0; n < num_acr122_usb_supported_device; n++) { - if ((acr122_usb_supported_devices[n].vendor_id == vendor_id) && - (acr122_usb_supported_devices[n].product_id == product_id)) { - strncpy(buffer, acr122_usb_supported_devices[n].name, len); - buffer[len - 1] = '\0'; - return true; - } - } - } - - return false; +acr122_usb_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len) +{ + *buffer = '\0'; + + usbbus_get_usb_device_name(dev, udev, buffer, len); + uint16_t vendor_id = usbbus_get_vendor_id(dev); + uint16_t product_id = usbbus_get_product_id(dev); + + if (!*buffer) { + for (size_t n = 0; n < num_acr122_usb_supported_device; n++) { + if ((acr122_usb_supported_devices[n].vendor_id == vendor_id) && + (acr122_usb_supported_devices[n].product_id == product_id)) { + strncpy(buffer, acr122_usb_supported_devices[n].name, len); + buffer[len - 1] = '\0'; + return true; + } + } + } + + return false; } static nfc_device * -acr122_usb_open(const nfc_context *context, const nfc_connstring connstring) { - nfc_device *pnd = NULL; - char *dev_address_str; - char *config_idx_str; - int connstring_decode_level = - connstring_decode(connstring, ACR122_USB_DRIVER_NAME, "usb", &dev_address_str, &config_idx_str); - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_DEBUG, - "%d element(s) have been decoded from \"%s\"", - connstring_decode_level, - connstring); - if (connstring_decode_level < 2) { - free(dev_address_str); - free(config_idx_str); - return NULL; - } - - uint8_t dev_addres = atoi(dev_address_str); - uint8_t config_idx = atoi(config_idx_str); - - usbbus_prepare(); - - struct acr122_usb_data data = { - .dev = NULL, - .pudh = NULL, - .configIdx = config_idx, - .uiEndPointIn = 0, - .uiEndPointOut = 0, - }; - - usbbus_get_device(dev_addres, &data.dev, &data.pudh); - // Reset device - libusb_reset_device(data.pudh); - - // Retrieve end points - usbbus_get_usb_endpoints(data.dev, &(data.uiEndPointIn), &(data.uiEndPointOut), &(data.uiMaxPacketSize)); - // Claim interface - int res = libusb_claim_interface(data.pudh, 0); - if (res < 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to claim USB interface (%s)", - libusb_strerror(res)); - libusb_close(data.pudh); - // we failed to use the specified device - free(dev_address_str); - free(config_idx_str); - return NULL; - } - - // Check if there are more than 0 alternative interfaces and claim the first one - // TODO would it not be better to iterate the alterative interfaces (and alternative settings) and check each one? - if (usbbus_get_num_alternate_settings(data.dev, data.configIdx) > 0) { - res = libusb_set_interface_alt_setting(data.pudh, 0, 0); - if (res < 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to set alternate setting on USB interface (%s)", - libusb_strerror(res)); - libusb_close(data.pudh); - // we failed to use the specified device - free(dev_address_str); - free(config_idx_str); - return NULL; - } - } - - // Allocate memory for the device info and specification, fill it and return the info - pnd = nfc_device_new(context, connstring); - if (!pnd) { - perror("malloc"); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - acr122_usb_get_usb_device_name(data.dev, data.pudh, pnd->name, sizeof(pnd->name)); - - pnd->driver_data = malloc(sizeof(struct acr122_usb_data)); - if (!pnd->driver_data) { - perror("malloc"); - nfc_device_free(pnd); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - *DRIVER_DATA(pnd) = data; - - // Alloc and init chip's data - if (pn53x_data_new(pnd, &acr122_usb_io) == NULL) { - perror("malloc"); - nfc_device_free(pnd); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - - memcpy(&(DRIVER_DATA(pnd)->tama_frame), acr122_usb_frame_template, sizeof(acr122_usb_frame_template)); - memcpy(&(DRIVER_DATA(pnd)->apdu_frame), acr122_usb_frame_template, sizeof(acr122_usb_frame_template)); - CHIP_DATA(pnd)->timer_correction = 46; // empirical tuning - pnd->driver = &acr122_usb_driver; - - if (acr122_usb_init(pnd) < 0) { - libusb_close(data.pudh); - nfc_device_free(pnd); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - DRIVER_DATA(pnd)->abort_flag = false; - - free(dev_address_str); - free(config_idx_str); - return pnd; +acr122_usb_open(const nfc_context *context, const nfc_connstring connstring) +{ + nfc_device *pnd = NULL; + char *dev_address_str; + char *config_idx_str; + int connstring_decode_level = + connstring_decode(connstring, ACR122_USB_DRIVER_NAME, "usb", &dev_address_str, &config_idx_str); + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_DEBUG, + "%d element(s) have been decoded from \"%s\"", + connstring_decode_level, + connstring); + if (connstring_decode_level < 2) { + free(dev_address_str); + free(config_idx_str); + return NULL; + } + + uint8_t dev_addres = atoi(dev_address_str); + uint8_t config_idx = atoi(config_idx_str); + + usbbus_prepare(); + + struct acr122_usb_data data = { + .dev = NULL, + .pudh = NULL, + .configIdx = config_idx, + .uiEndPointIn = 0, + .uiEndPointOut = 0, + }; + + usbbus_get_device(dev_addres, &data.dev, &data.pudh); + // Reset device + libusb_reset_device(data.pudh); + + // Retrieve end points + usbbus_get_usb_endpoints(data.dev, &(data.uiEndPointIn), &(data.uiEndPointOut), &(data.uiMaxPacketSize)); + // Claim interface + int res = libusb_claim_interface(data.pudh, 0); + if (res < 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to claim USB interface (%s)", + libusb_strerror(res)); + libusb_close(data.pudh); + // we failed to use the specified device + free(dev_address_str); + free(config_idx_str); + return NULL; + } + + // Check if there are more than 0 alternative interfaces and claim the first one + // TODO would it not be better to iterate the alterative interfaces (and alternative settings) and check each one? + if (usbbus_get_num_alternate_settings(data.dev, data.configIdx) > 0) { + res = libusb_set_interface_alt_setting(data.pudh, 0, 0); + if (res < 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to set alternate setting on USB interface (%s)", + libusb_strerror(res)); + libusb_close(data.pudh); + // we failed to use the specified device + free(dev_address_str); + free(config_idx_str); + return NULL; + } + } + + // Allocate memory for the device info and specification, fill it and return the info + pnd = nfc_device_new(context, connstring); + if (!pnd) { + perror("malloc"); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + acr122_usb_get_usb_device_name(data.dev, data.pudh, pnd->name, sizeof(pnd->name)); + + pnd->driver_data = malloc(sizeof(struct acr122_usb_data)); + if (!pnd->driver_data) { + perror("malloc"); + nfc_device_free(pnd); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + *DRIVER_DATA(pnd) = data; + + // Alloc and init chip's data + if (pn53x_data_new(pnd, &acr122_usb_io) == NULL) { + perror("malloc"); + nfc_device_free(pnd); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + + memcpy(&(DRIVER_DATA(pnd)->tama_frame), acr122_usb_frame_template, sizeof(acr122_usb_frame_template)); + memcpy(&(DRIVER_DATA(pnd)->apdu_frame), acr122_usb_frame_template, sizeof(acr122_usb_frame_template)); + CHIP_DATA(pnd)->timer_correction = 46; // empirical tuning + pnd->driver = &acr122_usb_driver; + + if (acr122_usb_init(pnd) < 0) { + libusb_close(data.pudh); + nfc_device_free(pnd); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + DRIVER_DATA(pnd)->abort_flag = false; + + free(dev_address_str); + free(config_idx_str); + return pnd; } static void -acr122_usb_close(nfc_device *pnd) { - acr122_usb_ack(pnd); - pn53x_idle(pnd); - - int res = libusb_release_interface(DRIVER_DATA(pnd)->pudh, 0); - if (res < 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to release USB interface (%s)", - libusb_strerror(res)); - } - - usbbus_close(DRIVER_DATA(pnd)->dev, DRIVER_DATA(pnd)->pudh); - - pn53x_data_free(pnd); - nfc_device_free(pnd); +acr122_usb_close(nfc_device *pnd) +{ + acr122_usb_ack(pnd); + pn53x_idle(pnd); + + int res = libusb_release_interface(DRIVER_DATA(pnd)->pudh, 0); + if (res < 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to release USB interface (%s)", + libusb_strerror(res)); + } + + usbbus_close(DRIVER_DATA(pnd)->dev, DRIVER_DATA(pnd)->pudh); + + pn53x_data_free(pnd); + nfc_device_free(pnd); } #if !defined(htole32) @@ -467,16 +473,17 @@ acr122_usb_close(nfc_device *pnd) { uint32_t htole32(uint32_t u32); uint32_t -htole32(uint32_t u32) { - union { - uint8_t arr[4]; - uint32_t u32; - } u; - for (int i = 0; i < 4; i++) { - u.arr[i] = (u32 & 0xff); - u32 >>= 8; - } - return u.u32; +htole32(uint32_t u32) +{ + union { + uint8_t arr[4]; + uint32_t u32; + } u; + for (int i = 0; i < 4; i++) { + u.arr[i] = (u32 & 0xff); + u32 >>= 8; + } + return u.u32; } #endif /* !defined(htole32) */ @@ -488,246 +495,263 @@ acr122_build_frame_from_apdu(nfc_device *pnd, const uint8_t p2, const uint8_t *data, const size_t data_len, - const uint8_t le) { - if (data_len > sizeof(DRIVER_DATA(pnd)->apdu_frame.apdu_payload)) - return NFC_EINVARG; - if ((data == NULL) && (data_len != 0)) - return NFC_EINVARG; - - DRIVER_DATA(pnd)->apdu_frame.ccid_header.dwLength = htole32(data_len + sizeof(struct apdu_header)); - DRIVER_DATA(pnd)->apdu_frame.apdu_header.bIns = ins; - DRIVER_DATA(pnd)->apdu_frame.apdu_header.bP1 = p1; - DRIVER_DATA(pnd)->apdu_frame.apdu_header.bP2 = p2; - if (data) { - // bLen is Lc when data != NULL - DRIVER_DATA(pnd)->apdu_frame.apdu_header.bLen = data_len; - memcpy(DRIVER_DATA(pnd)->apdu_frame.apdu_payload, data, data_len); - } else { - // bLen is Le when no data. - DRIVER_DATA(pnd)->apdu_frame.apdu_header.bLen = le; - } - return (sizeof(struct ccid_header) + sizeof(struct apdu_header) + data_len); + const uint8_t le) +{ + if (data_len > sizeof(DRIVER_DATA(pnd)->apdu_frame.apdu_payload)) + return NFC_EINVARG; + if ((data == NULL) && (data_len != 0)) + return NFC_EINVARG; + + DRIVER_DATA(pnd)->apdu_frame.ccid_header.dwLength = htole32(data_len + sizeof(struct apdu_header)); + DRIVER_DATA(pnd)->apdu_frame.apdu_header.bIns = ins; + DRIVER_DATA(pnd)->apdu_frame.apdu_header.bP1 = p1; + DRIVER_DATA(pnd)->apdu_frame.apdu_header.bP2 = p2; + if (data) { + // bLen is Lc when data != NULL + DRIVER_DATA(pnd)->apdu_frame.apdu_header.bLen = data_len; + memcpy(DRIVER_DATA(pnd)->apdu_frame.apdu_payload, data, data_len); + } else { + // bLen is Le when no data. + DRIVER_DATA(pnd)->apdu_frame.apdu_header.bLen = le; + } + return (sizeof(struct ccid_header) + sizeof(struct apdu_header) + data_len); } static int -acr122_build_frame_from_tama(nfc_device *pnd, const uint8_t *tama, const size_t tama_len) { - if (tama_len > sizeof(DRIVER_DATA(pnd)->tama_frame.tama_payload)) - return NFC_EINVARG; - - DRIVER_DATA(pnd)->tama_frame.ccid_header.dwLength = htole32(tama_len + sizeof(struct apdu_header) + 1); - DRIVER_DATA(pnd)->tama_frame.apdu_header.bLen = tama_len + 1; - memcpy(DRIVER_DATA(pnd)->tama_frame.tama_payload, tama, tama_len); - return (sizeof(struct ccid_header) + sizeof(struct apdu_header) + 1 + tama_len); +acr122_build_frame_from_tama(nfc_device *pnd, const uint8_t *tama, const size_t tama_len) +{ + if (tama_len > sizeof(DRIVER_DATA(pnd)->tama_frame.tama_payload)) + return NFC_EINVARG; + + DRIVER_DATA(pnd)->tama_frame.ccid_header.dwLength = htole32(tama_len + sizeof(struct apdu_header) + 1); + DRIVER_DATA(pnd)->tama_frame.apdu_header.bLen = tama_len + 1; + memcpy(DRIVER_DATA(pnd)->tama_frame.tama_payload, tama, tama_len); + return (sizeof(struct ccid_header) + sizeof(struct apdu_header) + 1 + tama_len); } static int -acr122_usb_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, const int timeout) { - int res; - if ((res = acr122_build_frame_from_tama(pnd, pbtData, szData)) < 0) { - pnd->last_error = NFC_EINVARG; - return pnd->last_error; - } - - if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), (unsigned char *) &(DRIVER_DATA(pnd)->tama_frame), res, timeout)) - < 0) { - pnd->last_error = res; - return pnd->last_error; - } - return NFC_SUCCESS; +acr122_usb_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, const int timeout) +{ + int res; + if ((res = acr122_build_frame_from_tama(pnd, pbtData, szData)) < 0) { + pnd->last_error = NFC_EINVARG; + return pnd->last_error; + } + + if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), (unsigned char *) & (DRIVER_DATA(pnd)->tama_frame), res, timeout)) + < 0) { + pnd->last_error = res; + return pnd->last_error; + } + return NFC_SUCCESS; } #define USBBUS_TIMEOUT_PER_PASS 200 static int -acr122_usb_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, const int timeout) { - off_t offset = 0; - - uint8_t abtRxBuf[255 + sizeof(struct ccid_header)]; - int res; - - /* - * If no timeout is specified but the command is blocking, force a 200ms (USBBUS_TIMEOUT_PER_PASS) - * timeout to allow breaking the loop if the user wants to stop it. - */ - int usbbus_timeout; - int remaining_time = timeout; - read: - if (timeout == USBBUS_INFINITE_TIMEOUT) { - usbbus_timeout = USBBUS_TIMEOUT_PER_PASS; - } else { - // A user-provided timeout is set, we have to cut it in multiple chunk to be able to keep an nfc_abort_command() mechanism - remaining_time -= USBBUS_TIMEOUT_PER_PASS; - if (remaining_time <= 0) { - pnd->last_error = NFC_ETIMEOUT; - return pnd->last_error; - } else { - usbbus_timeout = MIN(remaining_time, USBBUS_TIMEOUT_PER_PASS); - } - } - - res = acr122_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), usbbus_timeout); - - uint8_t attempted_response = RDR_to_PC_DataBlock; - size_t len; - - if (res == NFC_ETIMEOUT) { - if (DRIVER_DATA(pnd)->abort_flag) { - DRIVER_DATA(pnd)->abort_flag = false; - acr122_usb_ack(pnd); - pnd->last_error = NFC_EOPABORTED; - return pnd->last_error; - } else { - goto read; - } - } - if (res < 12) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Invalid RDR_to_PC_DataBlock frame"); - // try to interrupt current device state - acr122_usb_ack(pnd); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - if (abtRxBuf[offset] != attempted_response) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame header mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset++; - - len = abtRxBuf[offset++]; - if (!((len > 1) && (abtRxBuf[10] == 0xd5))) { // In case we didn't get an immediate answer: - if (len != 2) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Wrong reply"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - if (abtRxBuf[10] != SW1_More_Data_Available) { - if ((abtRxBuf[10] == SW1_Warning_with_NV_changed) - && (abtRxBuf[11] == PN53x_Specific_Application_Level_Error_Code)) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "%s", - "PN532 has detected an error at the application level"); - } else if ((abtRxBuf[10] == SW1_Warning_with_NV_changed) && (abtRxBuf[11] == 0x00)) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "PN532 didn't reply"); - } else { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unexpected Status Word (SW1: %02x SW2: %02x)", - abtRxBuf[10], - abtRxBuf[11]); - } - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - res = acr122_usb_send_apdu(pnd, - APDU_GetAdditionnalData, - 0x00, - 0x00, - NULL, - 0, - abtRxBuf[11], - abtRxBuf, - sizeof(abtRxBuf)); - if (res == NFC_ETIMEOUT) { - if (DRIVER_DATA(pnd)->abort_flag) { - DRIVER_DATA(pnd)->abort_flag = false; - acr122_usb_ack(pnd); - pnd->last_error = NFC_EOPABORTED; - return pnd->last_error; - } else { - goto read; // FIXME May cause some trouble on Touchatag, right ? - } - } - if (res < 12) { - // try to interrupt current device state - acr122_usb_ack(pnd); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - } - offset = 0; - if (abtRxBuf[offset] != attempted_response) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame header mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset++; - - // XXX In CCID specification, len is a 32-bits (dword), do we need to decode more than 1 byte ? (0-255 bytes for PN532 reply) - len = abtRxBuf[offset++]; - if ((abtRxBuf[offset] != 0x00) && (abtRxBuf[offset + 1] != 0x00) && (abtRxBuf[offset + 2] != 0x00)) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "%s", - "Not implemented: only 1-byte length is supported, please report this bug with a full trace."); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 3; - - if (len < 4) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Too small reply"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - len -= - 4; // We skip 2 bytes for PN532 direction byte (D5) and command byte (CMD+1), then 2 bytes for APDU status (90 00). - - if (len > szDataLen) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to receive data: buffer too small. (szDataLen: %" PRIuPTR ", len: %" PRIuPTR ")", - szDataLen, - len); - pnd->last_error = NFC_EOVFLOW; - return pnd->last_error; - } - - // Skip CCID remaining bytes - offset += 2; // bSlot and bSeq are not used - offset += 2; // XXX bStatus and bError should maybe checked ? - offset += 1; // bRFU should be 0x00 - - // TFI + PD0 (CC+1) - if (abtRxBuf[offset] != 0xD5) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "TFI Mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 1; - - if (abtRxBuf[offset] != CHIP_DATA(pnd)->last_command + 1) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Command Code verification failed"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 1; - - memcpy(pbtData, abtRxBuf + offset, len); - - return len; +acr122_usb_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, const int timeout) +{ + off_t offset = 0; + + uint8_t abtRxBuf[255 + sizeof(struct ccid_header)]; + int res; + + /* + * If no timeout is specified but the command is blocking, force a 200ms (USBBUS_TIMEOUT_PER_PASS) + * timeout to allow breaking the loop if the user wants to stop it. + */ + int usbbus_timeout; + int remaining_time = timeout; +read: + if (timeout == USBBUS_INFINITE_TIMEOUT) { + usbbus_timeout = USBBUS_TIMEOUT_PER_PASS; + } else { + // A user-provided timeout is set, we have to cut it in multiple chunk to be able to keep an nfc_abort_command() mechanism + remaining_time -= USBBUS_TIMEOUT_PER_PASS; + if (remaining_time <= 0) { + pnd->last_error = NFC_ETIMEOUT; + return pnd->last_error; + } else { + usbbus_timeout = MIN(remaining_time, USBBUS_TIMEOUT_PER_PASS); + } + } + + res = acr122_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), usbbus_timeout); + + uint8_t attempted_response = RDR_to_PC_DataBlock; + size_t len; + int error, status; + + if (res == NFC_ETIMEOUT) { + if (DRIVER_DATA(pnd)->abort_flag) { + DRIVER_DATA(pnd)->abort_flag = false; + acr122_usb_ack(pnd); + pnd->last_error = NFC_EOPABORTED; + return pnd->last_error; + } else { + goto read; + } + } + if (res < 10) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Invalid RDR_to_PC_DataBlock frame"); + // try to interrupt current device state + acr122_usb_ack(pnd); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + if (abtRxBuf[offset] != attempted_response) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame header mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset++; + + len = abtRxBuf[offset++]; + status = abtRxBuf[7]; + error = abtRxBuf[8]; + if (len == 0 && error == 0xFE) { // ICC_MUTE; XXX check for more errors + // Do not check status; my ACR122U seemingly has status=0 in this case, + // even though the spec says it should have had bmCommandStatus=1 + // and bmICCStatus=1. + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "Command timed out"); + pnd->last_error = NFC_ETIMEOUT; + return pnd->last_error; + } + + if (!((len > 1) && (abtRxBuf[10] == 0xd5))) { // In case we didn't get an immediate answer: + if (len != 2) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Wrong reply"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + if (abtRxBuf[10] != SW1_More_Data_Available) { + if ((abtRxBuf[10] == SW1_Warning_with_NV_changed) + && (abtRxBuf[11] == PN53x_Specific_Application_Level_Error_Code)) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "%s", + "PN532 has detected an error at the application level"); + } else if ((abtRxBuf[10] == SW1_Warning_with_NV_changed) && (abtRxBuf[11] == 0x00)) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "PN532 didn't reply"); + } else { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unexpected Status Word (SW1: %02x SW2: %02x)", + abtRxBuf[10], + abtRxBuf[11]); + } + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + res = acr122_usb_send_apdu(pnd, + APDU_GetAdditionnalData, + 0x00, + 0x00, + NULL, + 0, + abtRxBuf[11], + abtRxBuf, + sizeof(abtRxBuf)); + if (res == NFC_ETIMEOUT) { + if (DRIVER_DATA(pnd)->abort_flag) { + DRIVER_DATA(pnd)->abort_flag = false; + acr122_usb_ack(pnd); + pnd->last_error = NFC_EOPABORTED; + return pnd->last_error; + } else { + goto read; // FIXME May cause some trouble on Touchatag, right ? + } + } + if (res < 10) { + // try to interrupt current device state + acr122_usb_ack(pnd); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + } + offset = 0; + if (abtRxBuf[offset] != attempted_response) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame header mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset++; + + // XXX In CCID specification, len is a 32-bits (dword), do we need to decode more than 1 byte ? (0-255 bytes for PN532 reply) + len = abtRxBuf[offset++]; + if ((abtRxBuf[offset] != 0x00) && (abtRxBuf[offset + 1] != 0x00) && (abtRxBuf[offset + 2] != 0x00)) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "%s", + "Not implemented: only 1-byte length is supported, please report this bug with a full trace."); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 3; + + if (len < 4) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Too small reply"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + len -= + 4; // We skip 2 bytes for PN532 direction byte (D5) and command byte (CMD+1), then 2 bytes for APDU status (90 00). + + if (len > szDataLen) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to receive data: buffer too small. (szDataLen: %" PRIuPTR ", len: %" PRIuPTR ")", + szDataLen, + len); + pnd->last_error = NFC_EOVFLOW; + return pnd->last_error; + } + + // Skip CCID remaining bytes + offset += 2; // bSlot and bSeq are not used + offset += 2; // bStatus and bError is partially checked + offset += 1; // bRFU should be 0x00 + + // TFI + PD0 (CC+1) + if (abtRxBuf[offset] != 0xD5) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "TFI Mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 1; + + if (abtRxBuf[offset] != CHIP_DATA(pnd)->last_command + 1) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Command Code verification failed"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 1; + + memcpy(pbtData, abtRxBuf + offset, len); + + return len; } int -acr122_usb_ack(nfc_device *pnd) { - (void) pnd; - int res = 0; - uint8_t acr122_ack_frame[] = - {GetFirmwareVersion}; // We can't send a PN532's ACK frame, so we use a normal command to cancel current command - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ACR122 Abort"); - if ((res = acr122_build_frame_from_tama(pnd, acr122_ack_frame, sizeof(acr122_ack_frame))) < 0) - return res; - if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), (unsigned char *) &(DRIVER_DATA(pnd)->tama_frame), res, 1000)) - < 0) - return res; - uint8_t abtRxBuf[255 + sizeof(struct ccid_header)]; - res = acr122_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), 1000); - return res; +acr122_usb_ack(nfc_device *pnd) +{ + (void) pnd; + int res = 0; + uint8_t acr122_ack_frame[] = + {GetFirmwareVersion}; // We can't send a PN532's ACK frame, so we use a normal command to cancel current command + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ACR122 Abort"); + if ((res = acr122_build_frame_from_tama(pnd, acr122_ack_frame, sizeof(acr122_ack_frame))) < 0) + return res; + if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), (unsigned char *) & (DRIVER_DATA(pnd)->tama_frame), res, 1000)) + < 0) + return res; + uint8_t abtRxBuf[255 + sizeof(struct ccid_header)]; + res = acr122_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), 1000); + return res; } static int @@ -739,123 +763,126 @@ acr122_usb_send_apdu(nfc_device *pnd, size_t data_len, const uint8_t le, uint8_t *out, - const size_t out_size) { - int res; - size_t frame_len = acr122_build_frame_from_apdu(pnd, ins, p1, p2, data, data_len, le); - if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), - (unsigned char *) &(DRIVER_DATA(pnd)->apdu_frame), - frame_len, - 1000)) < 0) - return res; - if ((res = acr122_usb_bulk_read(DRIVER_DATA(pnd), out, out_size, 1000)) < 0) - return res; - return res; + const size_t out_size) +{ + int res; + size_t frame_len = acr122_build_frame_from_apdu(pnd, ins, p1, p2, data, data_len, le); + if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), + (unsigned char *) & (DRIVER_DATA(pnd)->apdu_frame), + frame_len, + 1000)) < 0) + return res; + if ((res = acr122_usb_bulk_read(DRIVER_DATA(pnd), out, out_size, 1000)) < 0) + return res; + return res; } int -acr122_usb_init(nfc_device *pnd) { - int res = 0; - int i; - uint8_t abtRxBuf[255 + sizeof(struct ccid_header)]; - - /* - // See ACR122 manual: "Bi-Color LED and Buzzer Control" section - uint8_t acr122u_get_led_state_frame[] = { - 0x6b, // CCID - 0x09, // lenght of frame - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding - // frame: - 0xff, // Class - 0x00, // INS - 0x40, // P1: Get LED state command - 0x00, // P2: LED state control - 0x04, // Lc - 0x00, 0x00, 0x00, 0x00, // Blinking duration control - }; - - log_put (LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ACR122 Get LED state"); - if ((res = acr122_usb_bulk_write (DRIVER_DATA (pnd), (uint8_t *) acr122u_get_led_state_frame, sizeof (acr122u_get_led_state_frame), 1000)) < 0) - return res; - - if ((res = acr122_usb_bulk_read (DRIVER_DATA (pnd), abtRxBuf, sizeof (abtRxBuf), 1000)) < 0) - return res; - */ - - if ((res = pn53x_set_property_int(pnd, NP_TIMEOUT_COMMAND, 1000)) < 0) - return res; - - // Power On ICC - uint8_t ccid_frame[] = { - PC_to_RDR_IccPowerOn, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 - }; - - if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), ccid_frame, sizeof(struct ccid_header), 1000)) < 0) - return res; - if ((res = acr122_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), 1000)) < 0) - return res; - - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ACR122 PICC Operating Parameters"); - if ((res = acr122_usb_send_apdu(pnd, 0x00, 0x51, 0x00, NULL, 0, 0, abtRxBuf, sizeof(abtRxBuf))) < 0) - return res; - - res = 0; - for (i = 0; i < 3; i++) { - if (res < 0) - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "PN532 init failed, trying again..."); - if ((res = pn53x_init(pnd)) >= 0) - break; - } - if (res < 0) - return res; - - return NFC_SUCCESS; +acr122_usb_init(nfc_device *pnd) +{ + int res = 0; + int i; + uint8_t abtRxBuf[255 + sizeof(struct ccid_header)]; + + /* + // See ACR122 manual: "Bi-Color LED and Buzzer Control" section + uint8_t acr122u_get_led_state_frame[] = { + 0x6b, // CCID + 0x09, // lenght of frame + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding + // frame: + 0xff, // Class + 0x00, // INS + 0x40, // P1: Get LED state command + 0x00, // P2: LED state control + 0x04, // Lc + 0x00, 0x00, 0x00, 0x00, // Blinking duration control + }; + + log_put (LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ACR122 Get LED state"); + if ((res = acr122_usb_bulk_write (DRIVER_DATA (pnd), (uint8_t *) acr122u_get_led_state_frame, sizeof (acr122u_get_led_state_frame), 1000)) < 0) + return res; + + if ((res = acr122_usb_bulk_read (DRIVER_DATA (pnd), abtRxBuf, sizeof (abtRxBuf), 1000)) < 0) + return res; + */ + + if ((res = pn53x_set_property_int(pnd, NP_TIMEOUT_COMMAND, 1000)) < 0) + return res; + + // Power On ICC + uint8_t ccid_frame[] = { + PC_to_RDR_IccPowerOn, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 + }; + + if ((res = acr122_usb_bulk_write(DRIVER_DATA(pnd), ccid_frame, sizeof(struct ccid_header), 1000)) < 0) + return res; + if ((res = acr122_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), 1000)) < 0) + return res; + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ACR122 PICC Operating Parameters"); + if ((res = acr122_usb_send_apdu(pnd, 0x00, 0x51, 0x00, NULL, 0, 0, abtRxBuf, sizeof(abtRxBuf))) < 0) + return res; + + res = 0; + for (i = 0; i < 3; i++) { + if (res < 0) + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "PN532 init failed, trying again..."); + if ((res = pn53x_init(pnd)) >= 0) + break; + } + if (res < 0) + return res; + + return NFC_SUCCESS; } static int -acr122_usb_abort_command(nfc_device *pnd) { - DRIVER_DATA(pnd)->abort_flag = true; - return NFC_SUCCESS; +acr122_usb_abort_command(nfc_device *pnd) +{ + DRIVER_DATA(pnd)->abort_flag = true; + return NFC_SUCCESS; } const struct pn53x_io acr122_usb_io = { - .send = acr122_usb_send, - .receive = acr122_usb_receive, + .send = acr122_usb_send, + .receive = acr122_usb_receive, }; const struct nfc_driver acr122_usb_driver = { - .name = ACR122_USB_DRIVER_NAME, - .scan_type = NOT_INTRUSIVE, - .scan = acr122_usb_scan, - .open = acr122_usb_open, - .close = acr122_usb_close, - .strerror = pn53x_strerror, - - .initiator_init = pn53x_initiator_init, - .initiator_init_secure_element = NULL, // No secure-element support - .initiator_select_passive_target = pn53x_initiator_select_passive_target, - .initiator_poll_target = pn53x_initiator_poll_target, - .initiator_select_dep_target = pn53x_initiator_select_dep_target, - .initiator_deselect_target = pn53x_initiator_deselect_target, - .initiator_transceive_bytes = pn53x_initiator_transceive_bytes, - .initiator_transceive_bits = pn53x_initiator_transceive_bits, - .initiator_transceive_bytes_timed = pn53x_initiator_transceive_bytes_timed, - .initiator_transceive_bits_timed = pn53x_initiator_transceive_bits_timed, - .initiator_target_is_present = pn53x_initiator_target_is_present, - - .target_init = pn53x_target_init, - .target_send_bytes = pn53x_target_send_bytes, - .target_receive_bytes = pn53x_target_receive_bytes, - .target_send_bits = pn53x_target_send_bits, - .target_receive_bits = pn53x_target_receive_bits, - - .device_set_property_bool = pn53x_set_property_bool, - .device_set_property_int = pn53x_set_property_int, - .get_supported_modulation = pn53x_get_supported_modulation, - .get_supported_baud_rate = pn53x_get_supported_baud_rate, - .device_get_information_about = pn53x_get_information_about, - - .abort_command = acr122_usb_abort_command, - .idle = pn53x_idle, - /* Even if PN532, PowerDown is not recommended on those devices */ - .powerdown = NULL, + .name = ACR122_USB_DRIVER_NAME, + .scan_type = NOT_INTRUSIVE, + .scan = acr122_usb_scan, + .open = acr122_usb_open, + .close = acr122_usb_close, + .strerror = pn53x_strerror, + + .initiator_init = pn53x_initiator_init, + .initiator_init_secure_element = NULL, // No secure-element support + .initiator_select_passive_target = pn53x_initiator_select_passive_target, + .initiator_poll_target = pn53x_initiator_poll_target, + .initiator_select_dep_target = pn53x_initiator_select_dep_target, + .initiator_deselect_target = pn53x_initiator_deselect_target, + .initiator_transceive_bytes = pn53x_initiator_transceive_bytes, + .initiator_transceive_bits = pn53x_initiator_transceive_bits, + .initiator_transceive_bytes_timed = pn53x_initiator_transceive_bytes_timed, + .initiator_transceive_bits_timed = pn53x_initiator_transceive_bits_timed, + .initiator_target_is_present = pn53x_initiator_target_is_present, + + .target_init = pn53x_target_init, + .target_send_bytes = pn53x_target_send_bytes, + .target_receive_bytes = pn53x_target_receive_bytes, + .target_send_bits = pn53x_target_send_bits, + .target_receive_bits = pn53x_target_receive_bits, + + .device_set_property_bool = pn53x_set_property_bool, + .device_set_property_int = pn53x_set_property_int, + .get_supported_modulation = pn53x_get_supported_modulation, + .get_supported_baud_rate = pn53x_get_supported_baud_rate, + .device_get_information_about = pn53x_get_information_about, + + .abort_command = acr122_usb_abort_command, + .idle = pn53x_idle, + /* Even if PN532, PowerDown is not recommended on those devices */ + .powerdown = NULL, }; diff --git a/libnfc/drivers/pn53x_usb.c b/libnfc/drivers/pn53x_usb.c index 2bed980e..136bddeb 100644 --- a/libnfc/drivers/pn53x_usb.c +++ b/libnfc/drivers/pn53x_usb.c @@ -66,27 +66,27 @@ Thanks to d18c7db and Okko for example code const nfc_modulation_type no_target_support[] = {0}; typedef enum { - UNKNOWN, - NXP_PN531, - SONY_PN531, - NXP_PN533, - ASK_LOGO, - SCM_SCL3711, - SCM_SCL3712, - SONY_RCS360 + UNKNOWN, + NXP_PN531, + SONY_PN531, + NXP_PN533, + ASK_LOGO, + SCM_SCL3711, + SCM_SCL3712, + SONY_RCS360 } pn53x_usb_model; // Internal data struct struct pn53x_usb_data { - libusb_device * dev; - libusb_device_handle *pudh; - uint8_t configIdx; - pn53x_usb_model model; - uint8_t uiEndPointIn; - uint8_t uiEndPointOut; - uint16_t uiMaxPacketSize; - volatile bool abort_flag; - bool possibly_corrupted_usbdesc; + libusb_device *dev; + libusb_device_handle *pudh; + uint8_t configIdx; + pn53x_usb_model model; + uint8_t uiEndPointIn; + uint8_t uiEndPointOut; + uint16_t uiMaxPacketSize; + volatile bool abort_flag; + bool possibly_corrupted_usbdesc; }; // Internal io struct @@ -97,62 +97,64 @@ bool pn53x_usb_get_usb_device_name(struct libusb_device *dev, libusb_device_hand int pn53x_usb_init(nfc_device *pnd); static int -pn53x_usb_bulk_read(struct pn53x_usb_data *data, uint8_t abtRx[], const size_t szRx, const int timeout) { - int actual_length; - int res = libusb_bulk_transfer(data->pudh, data->uiEndPointIn & 0xff, abtRx, szRx, &actual_length, timeout); - if (res == 0) { - LOG_HEX(NFC_LOG_GROUP_COM, "RX", abtRx, actual_length); - res = actual_length; - } else { - if (res != LIBUSB_ERROR_TIMEOUT) - log_put(NFC_LOG_GROUP_COM, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to read from USB (%s)", - libusb_strerror(res)); - } - return res; +pn53x_usb_bulk_read(struct pn53x_usb_data *data, uint8_t abtRx[], const size_t szRx, const int timeout) +{ + int actual_length; + int res = libusb_bulk_transfer(data->pudh, data->uiEndPointIn & 0xff, abtRx, szRx, &actual_length, timeout); + if (res == 0) { + LOG_HEX(NFC_LOG_GROUP_COM, "RX", abtRx, actual_length); + res = actual_length; + } else { + if (res != LIBUSB_ERROR_TIMEOUT) + log_put(NFC_LOG_GROUP_COM, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to read from USB (%s)", + libusb_strerror(res)); + } + return res; } static int -pn53x_usb_bulk_write(struct pn53x_usb_data *data, uint8_t abtTx[], const size_t szTx, const int timeout) { - LOG_HEX(NFC_LOG_GROUP_COM, "TX", abtTx, szTx); - int actual_length; - int res = libusb_bulk_transfer(data->pudh, data->uiEndPointOut & 0xff, abtTx, szTx, &actual_length, timeout); - if (res == 0) { - // HACK This little hack is a well know problem of USB, see http://www.libusb.org/ticket/6 for more details - if ((actual_length > 0) && ((actual_length % data->uiMaxPacketSize) == 0)) { - libusb_bulk_transfer(data->pudh, data->uiEndPointOut & 0xff, EMPTY_STRING, 0, &actual_length, timeout); - } - } else { - log_put(NFC_LOG_GROUP_COM, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to write to USB (%s)", - libusb_strerror(res)); - } - return res; +pn53x_usb_bulk_write(struct pn53x_usb_data *data, uint8_t abtTx[], const size_t szTx, const int timeout) +{ + LOG_HEX(NFC_LOG_GROUP_COM, "TX", abtTx, szTx); + int actual_length; + int res = libusb_bulk_transfer(data->pudh, data->uiEndPointOut & 0xff, abtTx, szTx, &actual_length, timeout); + if (res == 0) { + // HACK This little hack is a well know problem of USB, see http://www.libusb.org/ticket/6 for more details + if ((actual_length > 0) && ((actual_length % data->uiMaxPacketSize) == 0)) { + libusb_bulk_transfer(data->pudh, data->uiEndPointOut & 0xff, EMPTY_STRING, 0, &actual_length, timeout); + } + } else { + log_put(NFC_LOG_GROUP_COM, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to write to USB (%s)", + libusb_strerror(res)); + } + return res; } struct pn53x_usb_supported_device { - uint16_t vendor_id; - uint16_t product_id; - pn53x_usb_model model; - const char *name; - /* hardcoded known values for buggy hardware whose configuration vanishes */ - uint32_t uiEndPointIn; - uint32_t uiEndPointOut; - uint32_t uiMaxPacketSize; + uint16_t vendor_id; + uint16_t product_id; + pn53x_usb_model model; + const char *name; + /* hardcoded known values for buggy hardware whose configuration vanishes */ + uint32_t uiEndPointIn; + uint32_t uiEndPointOut; + uint32_t uiMaxPacketSize; }; const struct pn53x_usb_supported_device pn53x_usb_supported_devices[] = { - {0x04CC, 0x0531, NXP_PN531, "Philips / PN531", 0x84, 0x04, 0x40}, - {0x04CC, 0x2533, NXP_PN533, "NXP / PN533", 0x84, 0x04, 0x40}, - {0x04E6, 0x5591, SCM_SCL3711, "SCM Micro / SCL3711-NFC&RW", 0x84, 0x04, 0x40}, - {0x04E6, 0x5594, SCM_SCL3712, "SCM Micro / SCL3712-NFC&RW", 0, 0, 0}, // to check on real device - {0x054c, 0x0193, SONY_PN531, "Sony / PN531", 0x84, 0x04, 0x40}, - {0x1FD3, 0x0608, ASK_LOGO, "ASK / LoGO", 0x84, 0x04, 0x40}, - {0x054C, 0x02E1, SONY_RCS360, "Sony / FeliCa S360 [PaSoRi]", 0x84, 0x04, 0x40} + {0x04CC, 0x0531, NXP_PN531, "Philips / PN531", 0x84, 0x04, 0x40}, + {0x04CC, 0x2533, NXP_PN533, "NXP / PN533", 0x84, 0x04, 0x40}, + {0x04E6, 0x5591, SCM_SCL3711, "SCM Micro / SCL3711-NFC&RW", 0x84, 0x04, 0x40}, + {0x04E6, 0x5594, SCM_SCL3712, "SCM Micro / SCL3712-NFC&RW", 0, 0, 0}, // to check on real device + {0x054c, 0x0193, SONY_PN531, "Sony / PN531", 0x84, 0x04, 0x40}, + {0x1FD3, 0x0608, ASK_LOGO, "ASK / LoGO", 0x84, 0x04, 0x40}, + {0x054C, 0x02E1, SONY_RCS360, "Sony / FeliCa S360 [PaSoRi]", 0x84, 0x04, 0x40} }; const size_t num_pn53x_usb_supported_devices = sizeof(pn53x_usb_supported_devices) / sizeof(struct pn53x_usb_supported_device); @@ -160,677 +162,695 @@ const size_t num_pn53x_usb_supported_devices = sizeof(pn53x_usb_supported_device // PN533 USB descriptors backup buffers const uint8_t btXramUsbDesc_scl3711[] = { - 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, 0x04, 0x00, - 0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, - 0x04, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00, 0x04, 0x1e, 0x03, 0x53, 0x00, - 0x43, 0x00, 0x4c, 0x00, 0x33, 0x00, 0x37, 0x00, 0x31, 0x00, 0x31, 0x00, - 0x2d, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x43, 0x00, 0x26, 0x00, 0x52, 0x00, - 0x57, + 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, 0x04, 0x00, + 0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, + 0x04, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00, 0x04, 0x1e, 0x03, 0x53, 0x00, + 0x43, 0x00, 0x4c, 0x00, 0x33, 0x00, 0x37, 0x00, 0x31, 0x00, 0x31, 0x00, + 0x2d, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x43, 0x00, 0x26, 0x00, 0x52, 0x00, + 0x57, }; const uint8_t btXramUsbDesc_nxppn533[] = { - 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, 0x04, 0x00, - 0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, - 0x04, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00, 0x04, 0x0c, 0x03, 0x50, 0x00, - 0x4e, 0x00, 0x35, 0x00, 0x33, 0x00, 0x33, 0x00, 0x04, 0x03, 0x09, 0x04, - 0x08, 0x03, 0x4e, 0x00, 0x58, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, + 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, 0x04, 0x00, + 0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, + 0x04, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00, 0x04, 0x0c, 0x03, 0x50, 0x00, + 0x4e, 0x00, 0x35, 0x00, 0x33, 0x00, 0x33, 0x00, 0x04, 0x03, 0x09, 0x04, + 0x08, 0x03, 0x4e, 0x00, 0x58, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, }; const uint8_t btXramUsbDesc_asklogo[] = { - 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x96, 0x09, 0x04, 0x00, - 0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, - 0x04, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00, 0x04, 0x0a, 0x03, 0x4c, 0x00, - 0x6f, 0x00, 0x47, 0x00, 0x4f, 0x00, 0x04, 0x03, 0x09, 0x04, 0x08, 0x03, - 0x41, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, + 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x96, 0x09, 0x04, 0x00, + 0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, + 0x04, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00, 0x04, 0x0a, 0x03, 0x4c, 0x00, + 0x6f, 0x00, 0x47, 0x00, 0x4f, 0x00, 0x04, 0x03, 0x09, 0x04, 0x08, 0x03, + 0x41, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, }; -static void pn533_fix_usbdesc(nfc_device *pnd) { - // PN533 USB descriptors may have been corrupted by large commands/responses - // so they need to be restored before closing usb connection. - // cf PN5331B3HNC270 Release Note - uint32_t szXramUsbDesc = 0; - uint8_t *btXramUsbDesc = NULL; - if (DRIVER_DATA(pnd)->model == NXP_PN533) { - btXramUsbDesc = (uint8_t *) btXramUsbDesc_nxppn533; - szXramUsbDesc = sizeof(btXramUsbDesc_nxppn533); - } else if (DRIVER_DATA(pnd)->model == SCM_SCL3711) { - btXramUsbDesc = (uint8_t *) btXramUsbDesc_scl3711; - szXramUsbDesc = sizeof(btXramUsbDesc_scl3711); - } else if (DRIVER_DATA(pnd)->model == ASK_LOGO) { - btXramUsbDesc = (uint8_t *) btXramUsbDesc_asklogo; - szXramUsbDesc = sizeof(btXramUsbDesc_asklogo); - } +static void pn533_fix_usbdesc(nfc_device *pnd) +{ + // PN533 USB descriptors may have been corrupted by large commands/responses + // so they need to be restored before closing usb connection. + // cf PN5331B3HNC270 Release Note + uint32_t szXramUsbDesc = 0; + uint8_t *btXramUsbDesc = NULL; + if (DRIVER_DATA(pnd)->model == NXP_PN533) { + btXramUsbDesc = (uint8_t *) btXramUsbDesc_nxppn533; + szXramUsbDesc = sizeof(btXramUsbDesc_nxppn533); + } else if (DRIVER_DATA(pnd)->model == SCM_SCL3711) { + btXramUsbDesc = (uint8_t *) btXramUsbDesc_scl3711; + szXramUsbDesc = sizeof(btXramUsbDesc_scl3711); + } else if (DRIVER_DATA(pnd)->model == ASK_LOGO) { + btXramUsbDesc = (uint8_t *) btXramUsbDesc_asklogo; + szXramUsbDesc = sizeof(btXramUsbDesc_asklogo); + } #define MAXSZXRAMUSBDESC 61 - if ((szXramUsbDesc == 0) || (MAXSZXRAMUSBDESC > 61)) - return; + if ((szXramUsbDesc == 0) || (MAXSZXRAMUSBDESC > 61)) + return; #if 0 - // Debug routine to check if corruption occurred: - // Don't read more regs at once or it will trigger the bug and corrupt what we're busy reading! - uint8_t abtCmdRR[] = { ReadRegister, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t nRRreg = ((sizeof(abtCmdRR) - 1) / 2); - uint8_t abtRxRR[1 + nRRreg]; - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "%s", "Checking USB descriptors corruption in XRAM"); - for (uint8_t i = 0x19, j = 0; i < 0x19 + szXramUsbDesc;) { - for (uint8_t k = 0; k < nRRreg; k++) { - abtCmdRR[(2 * k) + 2] = i++; - } - if (pn53x_transceive(pnd, abtCmdRR, sizeof(abtCmdRR), abtRxRR, sizeof(abtRxRR), -1) < 0) { - return; // void - } - for (int k = 0; (k < nRRreg) && (j < szXramUsbDesc); k++) { - //printf("0x%02x, ", abtRxRR[1 + k]); - if (btXramUsbDesc[j] != abtRxRR[1 + k]) - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "XRAM corruption @ addr 0x00%02X: got %02x, expected %02x", 0x0019 + (j - 1), abtRxRR[1 + k], btXramUsbDesc[j]); - j++; - } - } + // Debug routine to check if corruption occurred: + // Don't read more regs at once or it will trigger the bug and corrupt what we're busy reading! + uint8_t abtCmdRR[] = { ReadRegister, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t nRRreg = ((sizeof(abtCmdRR) - 1) / 2); + uint8_t abtRxRR[1 + nRRreg]; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "%s", "Checking USB descriptors corruption in XRAM"); + for (uint8_t i = 0x19, j = 0; i < 0x19 + szXramUsbDesc;) { + for (uint8_t k = 0; k < nRRreg; k++) { + abtCmdRR[(2 * k) + 2] = i++; + } + if (pn53x_transceive(pnd, abtCmdRR, sizeof(abtCmdRR), abtRxRR, sizeof(abtRxRR), -1) < 0) { + return; // void + } + for (int k = 0; (k < nRRreg) && (j < szXramUsbDesc); k++) { + //printf("0x%02x, ", abtRxRR[1 + k]); + if (btXramUsbDesc[j] != abtRxRR[1 + k]) + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "XRAM corruption @ addr 0x00%02X: got %02x, expected %02x", 0x0019 + (j - 1), abtRxRR[1 + k], btXramUsbDesc[j]); + j++; + } + } #endif - // Abuse the overflow bug to restore USB descriptors in one go - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "%s", "Fixing USB descriptors corruption"); - uint8_t abtCmdWR[19 + MAXSZXRAMUSBDESC] = {GetFirmwareVersion}; - for (size_t i = 0; i < szXramUsbDesc; i++) { - abtCmdWR[i + 19] = btXramUsbDesc[i]; - } - size_t szCmdWR = sizeof(abtCmdWR); - uint8_t abtRxWR[4]; - if (pn53x_transceive(pnd, abtCmdWR, szCmdWR, abtRxWR, sizeof(abtRxWR), -1) < 0) { - return; // void - } - DRIVER_DATA(pnd)->possibly_corrupted_usbdesc = false; + // Abuse the overflow bug to restore USB descriptors in one go + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "%s", "Fixing USB descriptors corruption"); + uint8_t abtCmdWR[19 + MAXSZXRAMUSBDESC] = {GetFirmwareVersion}; + for (size_t i = 0; i < szXramUsbDesc; i++) { + abtCmdWR[i + 19] = btXramUsbDesc[i]; + } + size_t szCmdWR = sizeof(abtCmdWR); + uint8_t abtRxWR[4]; + if (pn53x_transceive(pnd, abtCmdWR, szCmdWR, abtRxWR, sizeof(abtRxWR), -1) < 0) { + return; // void + } + DRIVER_DATA(pnd)->possibly_corrupted_usbdesc = false; } static pn53x_usb_model -pn53x_usb_get_device_model(uint16_t vendor_id, uint16_t product_id) { - for (size_t n = 0; n < sizeof(pn53x_usb_supported_devices) / sizeof(struct pn53x_usb_supported_device); n++) { - if ((vendor_id == pn53x_usb_supported_devices[n].vendor_id) && - (product_id == pn53x_usb_supported_devices[n].product_id)) - return pn53x_usb_supported_devices[n].model; - } - - return UNKNOWN; +pn53x_usb_get_device_model(uint16_t vendor_id, uint16_t product_id) +{ + for (size_t n = 0; n < sizeof(pn53x_usb_supported_devices) / sizeof(struct pn53x_usb_supported_device); n++) { + if ((vendor_id == pn53x_usb_supported_devices[n].vendor_id) && + (product_id == pn53x_usb_supported_devices[n].product_id)) + return pn53x_usb_supported_devices[n].model; + } + + return UNKNOWN; } static bool -pn53x_usb_get_end_points_default(struct pn53x_usb_data *data) { - struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(data->dev, &descriptor); - - for (size_t n = 0; n < sizeof(pn53x_usb_supported_devices) / sizeof(struct pn53x_usb_supported_device); n++) { - if ((descriptor.idVendor == pn53x_usb_supported_devices[n].vendor_id) && - (descriptor.idProduct == pn53x_usb_supported_devices[n].product_id)) { - if (pn53x_usb_supported_devices[n].uiMaxPacketSize != 0) { - data->uiEndPointIn = pn53x_usb_supported_devices[n].uiEndPointIn; - data->uiEndPointOut = pn53x_usb_supported_devices[n].uiEndPointOut; - data->uiMaxPacketSize = pn53x_usb_supported_devices[n].uiMaxPacketSize; - - return true; - } - } - } - - return false; +pn53x_usb_get_end_points_default(struct pn53x_usb_data *data) +{ + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(data->dev, &descriptor); + + for (size_t n = 0; n < sizeof(pn53x_usb_supported_devices) / sizeof(struct pn53x_usb_supported_device); n++) { + if ((descriptor.idVendor == pn53x_usb_supported_devices[n].vendor_id) && + (descriptor.idProduct == pn53x_usb_supported_devices[n].product_id)) { + if (pn53x_usb_supported_devices[n].uiMaxPacketSize != 0) { + data->uiEndPointIn = pn53x_usb_supported_devices[n].uiEndPointIn; + data->uiEndPointOut = pn53x_usb_supported_devices[n].uiEndPointOut; + data->uiMaxPacketSize = pn53x_usb_supported_devices[n].uiMaxPacketSize; + + return true; + } + } + } + + return false; } int pn53x_usb_ack(nfc_device *pnd); static size_t -pn53x_usb_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) { - struct usbbus_device devices[num_pn53x_usb_supported_devices]; - for (size_t i = 0; i < num_pn53x_usb_supported_devices; i++) { - devices[i].product_id = pn53x_usb_supported_devices[i].product_id; - devices[i].vendor_id = pn53x_usb_supported_devices[i].vendor_id; - devices[i].name = pn53x_usb_supported_devices[i].name; - devices[i].max_packet_size = pn53x_usb_supported_devices[i].uiMaxPacketSize; - } - return usbbus_usb_scan(connstrings, connstrings_len, devices, num_pn53x_usb_supported_devices, PN53X_USB_DRIVER_NAME); +pn53x_usb_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) +{ + struct usbbus_device devices[num_pn53x_usb_supported_devices]; + for (size_t i = 0; i < num_pn53x_usb_supported_devices; i++) { + devices[i].product_id = pn53x_usb_supported_devices[i].product_id; + devices[i].vendor_id = pn53x_usb_supported_devices[i].vendor_id; + devices[i].name = pn53x_usb_supported_devices[i].name; + devices[i].max_packet_size = pn53x_usb_supported_devices[i].uiMaxPacketSize; + } + return usbbus_usb_scan(connstrings, connstrings_len, devices, num_pn53x_usb_supported_devices, PN53X_USB_DRIVER_NAME); } bool -pn53x_usb_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len) { - *buffer = '\0'; - - usbbus_get_usb_device_name(dev, udev, buffer, len); - uint16_t vendor_id = usbbus_get_vendor_id(dev); - uint16_t product_id = usbbus_get_product_id(dev); - - if (!*buffer) { - for (size_t n = 0; n < num_pn53x_usb_supported_devices; n++) { - if ((pn53x_usb_supported_devices[n].vendor_id == vendor_id) && - (pn53x_usb_supported_devices[n].product_id == product_id)) { - strncpy(buffer, pn53x_usb_supported_devices[n].name, len); - buffer[len - 1] = '\0'; - return true; - } - } - } - - return false; +pn53x_usb_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len) +{ + *buffer = '\0'; + + usbbus_get_usb_device_name(dev, udev, buffer, len); + uint16_t vendor_id = usbbus_get_vendor_id(dev); + uint16_t product_id = usbbus_get_product_id(dev); + + if (!*buffer) { + for (size_t n = 0; n < num_pn53x_usb_supported_devices; n++) { + if ((pn53x_usb_supported_devices[n].vendor_id == vendor_id) && + (pn53x_usb_supported_devices[n].product_id == product_id)) { + strncpy(buffer, pn53x_usb_supported_devices[n].name, len); + buffer[len - 1] = '\0'; + return true; + } + } + } + + return false; } static nfc_device * -pn53x_usb_open(const nfc_context *context, const nfc_connstring connstring) { - nfc_device *pnd = NULL; - - char *dev_address_str; - char *config_idx_str; - int connstring_decode_level = - connstring_decode(connstring, PN53X_USB_DRIVER_NAME, "usb", &dev_address_str, &config_idx_str); - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_DEBUG, - "%d element(s) have been decoded from \"%s\"", - connstring_decode_level, - connstring); - // At least the driver and the dev address need to be decoded - if (connstring_decode_level < 2) { - return NULL; - } - - uint8_t dev_addres = atoi(dev_address_str); - uint8_t config_idx = atoi(config_idx_str); - - usbbus_prepare(); - - - struct pn53x_usb_data data = { - .dev = NULL, - .pudh = NULL, - .configIdx = config_idx, - .uiEndPointIn = 0, - .uiEndPointOut = 0, - .possibly_corrupted_usbdesc = false, - }; - - usbbus_get_device(dev_addres, &data.dev, &data.pudh); - - // Retrieve end points, using hardcoded defaults if available - // or using the descriptors otherwise. - if (!pn53x_usb_get_end_points_default(&data)) { - // Find transfer endpoints for bulk transfers - usbbus_get_usb_endpoints(data.dev, &(data.uiEndPointIn), &(data.uiEndPointOut), &(data.uiMaxPacketSize)); - - } - // Set configuration - int res = libusb_set_configuration(data.pudh, data.configIdx); - if (res < 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to set USB configuration (%s)", - libusb_strerror(res)); - if (res == LIBUSB_ERROR_ACCESS) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_INFO, - "Warning: Please double check USB permissions for device %04x:%04x:%03d", - usbbus_get_vendor_id(data.dev), - usbbus_get_product_id(data.dev), - data.configIdx); - } - // we failed to use the specified device - usbbus_close(data.dev, data.pudh); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - - res = libusb_claim_interface(data.pudh, 0); - if (res < 0) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to claim USB interface (%s)", - libusb_strerror(res)); - // we failed to use the specified device - usbbus_close(data.dev, data.pudh); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - data.model = pn53x_usb_get_device_model(usbbus_get_vendor_id(data.dev), usbbus_get_product_id(data.dev)); - // Allocate memory for the device info and specification, fill it and return the info - pnd = nfc_device_new(context, connstring); - if (!pnd) { - perror("malloc"); - usbbus_close(data.dev, data.pudh); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - pn53x_usb_get_usb_device_name(data.dev, data.pudh, pnd->name, sizeof(pnd->name)); - - pnd->driver_data = malloc(sizeof(struct pn53x_usb_data)); - if (!pnd->driver_data) { - perror("malloc"); - nfc_device_free(pnd); - usbbus_close(data.dev, data.pudh); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - *DRIVER_DATA(pnd) = data; - - // Alloc and init chip's data - if (pn53x_data_new(pnd, &pn53x_usb_io) == NULL) { - perror("malloc"); - nfc_device_free(pnd); - usbbus_close(data.dev, data.pudh); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - - switch (DRIVER_DATA(pnd)->model) { - // empirical tuning - case ASK_LOGO:CHIP_DATA(pnd)->timer_correction = 50; - CHIP_DATA(pnd)->progressive_field = true; - break; - case SCM_SCL3711: - case SCM_SCL3712: - case NXP_PN533:CHIP_DATA(pnd)->timer_correction = 46; - break; - case NXP_PN531:CHIP_DATA(pnd)->timer_correction = 50; - break; - case SONY_PN531:CHIP_DATA(pnd)->timer_correction = 54; - break; - case SONY_RCS360: - case UNKNOWN: - CHIP_DATA(pnd)->timer_correction = 0; // TODO: allow user to know if timed functions are available - break; - } - pnd->driver = &pn53x_usb_driver; - - // HACK1: Send first an ACK as Abort command, to reset chip before talking to it: - pn53x_usb_ack(pnd); - - // HACK2: Then send a GetFirmware command to resync USB toggle bit between host & device - // in case host used set_configuration and expects the device to have reset its toggle bit, which PN53x doesn't do - if (pn53x_usb_init(pnd) < 0) { - nfc_device_free(pnd); - usbbus_close(data.dev, data.pudh); - free(dev_address_str); - free(config_idx_str); - return NULL; - } - DRIVER_DATA(pnd)->abort_flag = false; - return pnd; +pn53x_usb_open(const nfc_context *context, const nfc_connstring connstring) +{ + nfc_device *pnd = NULL; + + char *dev_address_str; + char *config_idx_str; + int connstring_decode_level = + connstring_decode(connstring, PN53X_USB_DRIVER_NAME, "usb", &dev_address_str, &config_idx_str); + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_DEBUG, + "%d element(s) have been decoded from \"%s\"", + connstring_decode_level, + connstring); + // At least the driver and the dev address need to be decoded + if (connstring_decode_level < 2) { + return NULL; + } + + uint8_t dev_addres = atoi(dev_address_str); + uint8_t config_idx = atoi(config_idx_str); + + usbbus_prepare(); + + + struct pn53x_usb_data data = { + .dev = NULL, + .pudh = NULL, + .configIdx = config_idx, + .uiEndPointIn = 0, + .uiEndPointOut = 0, + .possibly_corrupted_usbdesc = false, + }; + + usbbus_get_device(dev_addres, &data.dev, &data.pudh); + + // Retrieve end points, using hardcoded defaults if available + // or using the descriptors otherwise. + if (!pn53x_usb_get_end_points_default(&data)) { + // Find transfer endpoints for bulk transfers + usbbus_get_usb_endpoints(data.dev, &(data.uiEndPointIn), &(data.uiEndPointOut), &(data.uiMaxPacketSize)); + + } + // Set configuration + int res = libusb_set_configuration(data.pudh, data.configIdx); + if (res < 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to set USB configuration (%s)", + libusb_strerror(res)); + if (res == LIBUSB_ERROR_ACCESS) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_INFO, + "Warning: Please double check USB permissions for device %04x:%04x:%03d", + usbbus_get_vendor_id(data.dev), + usbbus_get_product_id(data.dev), + data.configIdx); + } + // we failed to use the specified device + usbbus_close(data.dev, data.pudh); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + + res = libusb_claim_interface(data.pudh, 0); + if (res < 0) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to claim USB interface (%s)", + libusb_strerror(res)); + // we failed to use the specified device + usbbus_close(data.dev, data.pudh); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + data.model = pn53x_usb_get_device_model(usbbus_get_vendor_id(data.dev), usbbus_get_product_id(data.dev)); + // Allocate memory for the device info and specification, fill it and return the info + pnd = nfc_device_new(context, connstring); + if (!pnd) { + perror("malloc"); + usbbus_close(data.dev, data.pudh); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + pn53x_usb_get_usb_device_name(data.dev, data.pudh, pnd->name, sizeof(pnd->name)); + + pnd->driver_data = malloc(sizeof(struct pn53x_usb_data)); + if (!pnd->driver_data) { + perror("malloc"); + nfc_device_free(pnd); + usbbus_close(data.dev, data.pudh); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + *DRIVER_DATA(pnd) = data; + + // Alloc and init chip's data + if (pn53x_data_new(pnd, &pn53x_usb_io) == NULL) { + perror("malloc"); + nfc_device_free(pnd); + usbbus_close(data.dev, data.pudh); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + + switch (DRIVER_DATA(pnd)->model) { + // empirical tuning + case ASK_LOGO: + CHIP_DATA(pnd)->timer_correction = 50; + CHIP_DATA(pnd)->progressive_field = true; + break; + case SCM_SCL3711: + case SCM_SCL3712: + case NXP_PN533: + CHIP_DATA(pnd)->timer_correction = 46; + break; + case NXP_PN531: + CHIP_DATA(pnd)->timer_correction = 50; + break; + case SONY_PN531: + CHIP_DATA(pnd)->timer_correction = 54; + break; + case SONY_RCS360: + case UNKNOWN: + CHIP_DATA(pnd)->timer_correction = 0; // TODO: allow user to know if timed functions are available + break; + } + pnd->driver = &pn53x_usb_driver; + + // HACK1: Send first an ACK as Abort command, to reset chip before talking to it: + pn53x_usb_ack(pnd); + + // HACK2: Then send a GetFirmware command to resync USB toggle bit between host & device + // in case host used set_configuration and expects the device to have reset its toggle bit, which PN53x doesn't do + if (pn53x_usb_init(pnd) < 0) { + nfc_device_free(pnd); + usbbus_close(data.dev, data.pudh); + free(dev_address_str); + free(config_idx_str); + return NULL; + } + DRIVER_DATA(pnd)->abort_flag = false; + return pnd; } static void -pn53x_usb_close(nfc_device *pnd) { - pn53x_usb_ack(pnd); +pn53x_usb_close(nfc_device *pnd) +{ + pn53x_usb_ack(pnd); - if (DRIVER_DATA(pnd)->model == ASK_LOGO) { - /* Set P30, P31, P32, P33, P35 to logic 1 and P34 to 0 logic */ - /* ie. Switch all LEDs off and turn off progressive field */ - pn53x_write_register(pnd, PN53X_SFR_P3, 0xFF, _BV(P30) | _BV(P31) | _BV(P32) | _BV(P33) | _BV(P35)); - } + if (DRIVER_DATA(pnd)->model == ASK_LOGO) { + /* Set P30, P31, P32, P33, P35 to logic 1 and P34 to 0 logic */ + /* ie. Switch all LEDs off and turn off progressive field */ + pn53x_write_register(pnd, PN53X_SFR_P3, 0xFF, _BV(P30) | _BV(P31) | _BV(P32) | _BV(P33) | _BV(P35)); + } - if (DRIVER_DATA(pnd)->possibly_corrupted_usbdesc) { - pn533_fix_usbdesc(pnd); - } + if (DRIVER_DATA(pnd)->possibly_corrupted_usbdesc) { + pn533_fix_usbdesc(pnd); + } - pn53x_idle(pnd); + pn53x_idle(pnd); - usbbus_close(DRIVER_DATA(pnd)->dev, DRIVER_DATA(pnd)->pudh); + usbbus_close(DRIVER_DATA(pnd)->dev, DRIVER_DATA(pnd)->pudh); - pn53x_data_free(pnd); - nfc_device_free(pnd); + pn53x_data_free(pnd); + nfc_device_free(pnd); } #define PN53X_USB_BUFFER_LEN (PN53x_EXTENDED_FRAME__DATA_MAX_LEN + PN53x_EXTENDED_FRAME__OVERHEAD) static int -pn53x_usb_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, const int timeout) { - uint8_t abtFrame[PN53X_USB_BUFFER_LEN] = {0x00, 0x00, 0xff}; // Every packet must start with "00 00 ff" - size_t szFrame = 0; - int res = 0; - - if ((res = pn53x_build_frame(abtFrame, &szFrame, pbtData, szData)) < 0) { - pnd->last_error = res; - return pnd->last_error; - } - - DRIVER_DATA(pnd)->possibly_corrupted_usbdesc |= szData > 17; - if ((res = pn53x_usb_bulk_write(DRIVER_DATA(pnd), abtFrame, szFrame, timeout)) < 0) { - pnd->last_error = res; - return pnd->last_error; - } - - uint8_t abtRxBuf[PN53X_USB_BUFFER_LEN]; - if ((res = pn53x_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), timeout)) < 0) { - // try to interrupt current device state - pn53x_usb_ack(pnd); - pnd->last_error = res; - return pnd->last_error; - } - - if (pn53x_check_ack_frame(pnd, abtRxBuf, res) == 0) { - // The PN53x is running the sent command - } else { - // For some reasons (eg. send another command while a previous one is - // running), the PN533 sometimes directly replies the response packet - // instead of ACK frame, so we send a NACK frame to force PN533 to resend - // response packet. With this hack, the next executed function (ie. - // pn53x_usb_receive()) will be able to retrieve the correct response - // packet. - // FIXME Sony reader is also affected by this bug but NACK is not supported - if ((res = pn53x_usb_bulk_write(DRIVER_DATA(pnd), - (uint8_t *) pn53x_nack_frame, - sizeof(pn53x_nack_frame), - timeout)) < 0) { - pnd->last_error = res; - // try to interrupt current device state - pn53x_usb_ack(pnd); - return pnd->last_error; - } - } - return NFC_SUCCESS; +pn53x_usb_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, const int timeout) +{ + uint8_t abtFrame[PN53X_USB_BUFFER_LEN] = {0x00, 0x00, 0xff}; // Every packet must start with "00 00 ff" + size_t szFrame = 0; + int res = 0; + + if ((res = pn53x_build_frame(abtFrame, &szFrame, pbtData, szData)) < 0) { + pnd->last_error = res; + return pnd->last_error; + } + + DRIVER_DATA(pnd)->possibly_corrupted_usbdesc |= szData > 17; + if ((res = pn53x_usb_bulk_write(DRIVER_DATA(pnd), abtFrame, szFrame, timeout)) < 0) { + pnd->last_error = res; + return pnd->last_error; + } + + uint8_t abtRxBuf[PN53X_USB_BUFFER_LEN]; + if ((res = pn53x_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), timeout)) < 0) { + // try to interrupt current device state + pn53x_usb_ack(pnd); + pnd->last_error = res; + return pnd->last_error; + } + + if (pn53x_check_ack_frame(pnd, abtRxBuf, res) == 0) { + // The PN53x is running the sent command + } else { + // For some reasons (eg. send another command while a previous one is + // running), the PN533 sometimes directly replies the response packet + // instead of ACK frame, so we send a NACK frame to force PN533 to resend + // response packet. With this hack, the next executed function (ie. + // pn53x_usb_receive()) will be able to retrieve the correct response + // packet. + // FIXME Sony reader is also affected by this bug but NACK is not supported + if ((res = pn53x_usb_bulk_write(DRIVER_DATA(pnd), + (uint8_t *) pn53x_nack_frame, + sizeof(pn53x_nack_frame), + timeout)) < 0) { + pnd->last_error = res; + // try to interrupt current device state + pn53x_usb_ack(pnd); + return pnd->last_error; + } + } + return NFC_SUCCESS; } #define USBBUS_TIMEOUT_PER_PASS 200 static int -pn53x_usb_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, const int timeout) { - size_t len; - off_t offset = 0; - - uint8_t abtRxBuf[PN53X_USB_BUFFER_LEN]; - int res; - - /* - * If no timeout is specified but the command is blocking, force a 200ms (USBBUS_TIMEOUT_PER_PASS) - * timeout to allow breaking the loop if the user wants to stop it. - */ - int usbbus_timeout; - int remaining_time = timeout; - read: - if (timeout == USBBUS_INFINITE_TIMEOUT) { - usbbus_timeout = USBBUS_TIMEOUT_PER_PASS; - } else { - // A user-provided timeout is set, we have to cut it in multiple chunk to be able to keep an nfc_abort_command() mechanism - remaining_time -= USBBUS_TIMEOUT_PER_PASS; - if (remaining_time <= 0) { - pnd->last_error = NFC_ETIMEOUT; - return pnd->last_error; - } else { - usbbus_timeout = MIN(remaining_time, USBBUS_TIMEOUT_PER_PASS); - } - } - - res = pn53x_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), usbbus_timeout); - - if (res == LIBUSB_ERROR_TIMEOUT) { - if (DRIVER_DATA(pnd)->abort_flag) { - DRIVER_DATA(pnd)->abort_flag = false; - pn53x_usb_ack(pnd); - pnd->last_error = NFC_EOPABORTED; - return pnd->last_error; - } else { - goto read; - } - } - - if (res < 0) { - // try to interrupt current device state - pn53x_usb_ack(pnd); - pnd->last_error = res; - return pnd->last_error; - } - - const uint8_t pn53x_preamble[3] = {0x00, 0x00, 0xff}; - if (0 != (memcmp(abtRxBuf, pn53x_preamble, 3))) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame preamble+start code mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 3; - - if ((0x01 == abtRxBuf[offset]) && (0xff == abtRxBuf[offset + 1])) { - // Error frame - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Application level error detected"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } else if ((0xff == abtRxBuf[offset]) && (0xff == abtRxBuf[offset + 1])) { - // Extended frame - offset += 2; - - // (abtRxBuf[offset] << 8) + abtRxBuf[offset + 1] (LEN) include TFI + (CC+1) - len = (abtRxBuf[offset] << 8) + abtRxBuf[offset + 1] - 2; - if (((abtRxBuf[offset] + abtRxBuf[offset + 1] + abtRxBuf[offset + 2]) % 256) != 0) { - // TODO: Retry - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 3; - } else { - // Normal frame - if (256 != (abtRxBuf[offset] + abtRxBuf[offset + 1])) { - // TODO: Retry - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - - // abtRxBuf[3] (LEN) include TFI + (CC+1) - len = abtRxBuf[offset] - 2; - offset += 2; - } - - if (len > szDataLen) { - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_ERROR, - "Unable to receive data: buffer too small. (szDataLen: %" PRIuPTR ", len: %" PRIuPTR ")", - szDataLen, - len); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - - // TFI + PD0 (CC+1) - if (abtRxBuf[offset] != 0xD5) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "TFI Mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 1; - - if (abtRxBuf[offset] != CHIP_DATA(pnd)->last_command + 1) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Command Code verification failed"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 1; - - memcpy(pbtData, abtRxBuf + offset, len); - offset += len; - - uint8_t btDCS = (256 - 0xD5); - btDCS -= CHIP_DATA(pnd)->last_command + 1; - for (size_t szPos = 0; szPos < len; szPos++) { - btDCS -= pbtData[szPos]; - } - - if (btDCS != abtRxBuf[offset]) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Data checksum mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - offset += 1; - - if (0x00 != abtRxBuf[offset]) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame postamble mismatch"); - pnd->last_error = NFC_EIO; - return pnd->last_error; - } - // The PN53x command is done and we successfully received the reply - pnd->last_error = 0; - DRIVER_DATA(pnd)->possibly_corrupted_usbdesc |= len > 16; - return len; +pn53x_usb_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, const int timeout) +{ + size_t len; + off_t offset = 0; + + uint8_t abtRxBuf[PN53X_USB_BUFFER_LEN]; + int res; + + /* + * If no timeout is specified but the command is blocking, force a 200ms (USBBUS_TIMEOUT_PER_PASS) + * timeout to allow breaking the loop if the user wants to stop it. + */ + int usbbus_timeout; + int remaining_time = timeout; +read: + if (timeout == USBBUS_INFINITE_TIMEOUT) { + usbbus_timeout = USBBUS_TIMEOUT_PER_PASS; + } else { + // A user-provided timeout is set, we have to cut it in multiple chunk to be able to keep an nfc_abort_command() mechanism + remaining_time -= USBBUS_TIMEOUT_PER_PASS; + if (remaining_time <= 0) { + pnd->last_error = NFC_ETIMEOUT; + return pnd->last_error; + } else { + usbbus_timeout = MIN(remaining_time, USBBUS_TIMEOUT_PER_PASS); + } + } + + res = pn53x_usb_bulk_read(DRIVER_DATA(pnd), abtRxBuf, sizeof(abtRxBuf), usbbus_timeout); + + if (res == LIBUSB_ERROR_TIMEOUT) { + if (DRIVER_DATA(pnd)->abort_flag) { + DRIVER_DATA(pnd)->abort_flag = false; + pn53x_usb_ack(pnd); + pnd->last_error = NFC_EOPABORTED; + return pnd->last_error; + } else { + goto read; + } + } + + if (res < 0) { + // try to interrupt current device state + pn53x_usb_ack(pnd); + pnd->last_error = res; + return pnd->last_error; + } + + const uint8_t pn53x_preamble[3] = {0x00, 0x00, 0xff}; + if (0 != (memcmp(abtRxBuf, pn53x_preamble, 3))) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame preamble+start code mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 3; + + if ((0x01 == abtRxBuf[offset]) && (0xff == abtRxBuf[offset + 1])) { + // Error frame + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Application level error detected"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } else if ((0xff == abtRxBuf[offset]) && (0xff == abtRxBuf[offset + 1])) { + // Extended frame + offset += 2; + + // (abtRxBuf[offset] << 8) + abtRxBuf[offset + 1] (LEN) include TFI + (CC+1) + len = (abtRxBuf[offset] << 8) + abtRxBuf[offset + 1] - 2; + if (((abtRxBuf[offset] + abtRxBuf[offset + 1] + abtRxBuf[offset + 2]) % 256) != 0) { + // TODO: Retry + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 3; + } else { + // Normal frame + if (256 != (abtRxBuf[offset] + abtRxBuf[offset + 1])) { + // TODO: Retry + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + + // abtRxBuf[3] (LEN) include TFI + (CC+1) + len = abtRxBuf[offset] - 2; + offset += 2; + } + + if (len > szDataLen) { + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_ERROR, + "Unable to receive data: buffer too small. (szDataLen: %" PRIuPTR ", len: %" PRIuPTR ")", + szDataLen, + len); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + + // TFI + PD0 (CC+1) + if (abtRxBuf[offset] != 0xD5) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "TFI Mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 1; + + if (abtRxBuf[offset] != CHIP_DATA(pnd)->last_command + 1) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Command Code verification failed"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 1; + + memcpy(pbtData, abtRxBuf + offset, len); + offset += len; + + uint8_t btDCS = (256 - 0xD5); + btDCS -= CHIP_DATA(pnd)->last_command + 1; + for (size_t szPos = 0; szPos < len; szPos++) { + btDCS -= pbtData[szPos]; + } + + if (btDCS != abtRxBuf[offset]) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Data checksum mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + offset += 1; + + if (0x00 != abtRxBuf[offset]) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame postamble mismatch"); + pnd->last_error = NFC_EIO; + return pnd->last_error; + } + // The PN53x command is done and we successfully received the reply + pnd->last_error = 0; + DRIVER_DATA(pnd)->possibly_corrupted_usbdesc |= len > 16; + return len; } int -pn53x_usb_ack(nfc_device *pnd) { - return pn53x_usb_bulk_write(DRIVER_DATA(pnd), (uint8_t *) pn53x_ack_frame, sizeof(pn53x_ack_frame), 1000); +pn53x_usb_ack(nfc_device *pnd) +{ + return pn53x_usb_bulk_write(DRIVER_DATA(pnd), (uint8_t *) pn53x_ack_frame, sizeof(pn53x_ack_frame), 1000); } int -pn53x_usb_init(nfc_device *pnd) { - int res = 0; - // Sometimes PN53x USB doesn't reply ACK one the first frame, so we need to send a dummy one... - //pn53x_check_communication (pnd); // Sony RC-S360 doesn't support this command for now so let's use a get_firmware_version instead: - const uint8_t abtCmd[] = {GetFirmwareVersion}; - pn53x_transceive(pnd, abtCmd, sizeof(abtCmd), NULL, 0, -1); - // ...and we don't care about error - pnd->last_error = 0; - if (SONY_RCS360 == DRIVER_DATA(pnd)->model) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "SONY RC-S360 initialization."); - const uint8_t abtCmd2[] = {0x18, 0x01}; - pn53x_transceive(pnd, abtCmd2, sizeof(abtCmd2), NULL, 0, -1); - pn53x_usb_ack(pnd); - } - - if ((res = pn53x_init(pnd)) < 0) - return res; - - if (ASK_LOGO == DRIVER_DATA(pnd)->model) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ASK LoGO initialization."); - /* Internal registers */ - /* Disable 100mA current limit, Power on Secure IC (SVDD) */ - pn53x_write_register(pnd, - PN53X_REG_Control_switch_rng, - 0xFF, - SYMBOL_CURLIMOFF | SYMBOL_SIC_SWITCH_EN | SYMBOL_RANDOM_DATAREADY); - /* Select the signal to be output on SIGOUT: Modulation signal (envelope) from the internal coder */ - pn53x_write_register(pnd, PN53X_REG_CIU_TxSel, 0xFF, 0x14); - - /* SFR Registers */ - /* Setup push-pulls for pins from P30 to P35 */ - pn53x_write_register(pnd, PN53X_SFR_P3CFGB, 0xFF, 0x37); - - /* - On ASK LoGO hardware: - LEDs port bits definition: - * LED 1: bit 2 (P32) - * LED 2: bit 1 (P31) - * LED 3: bit 0 or 3 (depending of hardware revision) (P30 or P33) - * LED 4: bit 5 (P35) - Notes: - * Set logical 0 to switch LED on; logical 1 to switch LED off. - * Bit 4 should be maintained at 1 to keep RF field on. - - Progressive field activation: - The ASK LoGO hardware can progressively power-up the antenna. - To use this feature we have to switch on the field by switching on - the field on PN533 (RFConfiguration) then set P34 to '1', and cut-off the - field by switching off the field on PN533 then set P34 to '0'. - */ - - /* Set P30, P31, P33, P35 to logic 1 and P32, P34 to 0 logic */ - /* ie. Switch LED1 on and turn off progressive field */ - pn53x_write_register(pnd, PN53X_SFR_P3, 0xFF, _BV(P30) | _BV(P31) | _BV(P33) | _BV(P35)); - } - if (DRIVER_DATA(pnd)->possibly_corrupted_usbdesc) - pn533_fix_usbdesc(pnd); - - return NFC_SUCCESS; +pn53x_usb_init(nfc_device *pnd) +{ + int res = 0; + // Sometimes PN53x USB doesn't reply ACK one the first frame, so we need to send a dummy one... + //pn53x_check_communication (pnd); // Sony RC-S360 doesn't support this command for now so let's use a get_firmware_version instead: + const uint8_t abtCmd[] = {GetFirmwareVersion}; + pn53x_transceive(pnd, abtCmd, sizeof(abtCmd), NULL, 0, -1); + // ...and we don't care about error + pnd->last_error = 0; + if (SONY_RCS360 == DRIVER_DATA(pnd)->model) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "SONY RC-S360 initialization."); + const uint8_t abtCmd2[] = {0x18, 0x01}; + pn53x_transceive(pnd, abtCmd2, sizeof(abtCmd2), NULL, 0, -1); + pn53x_usb_ack(pnd); + } + + if ((res = pn53x_init(pnd)) < 0) + return res; + + if (ASK_LOGO == DRIVER_DATA(pnd)->model) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "ASK LoGO initialization."); + /* Internal registers */ + /* Disable 100mA current limit, Power on Secure IC (SVDD) */ + pn53x_write_register(pnd, + PN53X_REG_Control_switch_rng, + 0xFF, + SYMBOL_CURLIMOFF | SYMBOL_SIC_SWITCH_EN | SYMBOL_RANDOM_DATAREADY); + /* Select the signal to be output on SIGOUT: Modulation signal (envelope) from the internal coder */ + pn53x_write_register(pnd, PN53X_REG_CIU_TxSel, 0xFF, 0x14); + + /* SFR Registers */ + /* Setup push-pulls for pins from P30 to P35 */ + pn53x_write_register(pnd, PN53X_SFR_P3CFGB, 0xFF, 0x37); + + /* + On ASK LoGO hardware: + LEDs port bits definition: + * LED 1: bit 2 (P32) + * LED 2: bit 1 (P31) + * LED 3: bit 0 or 3 (depending of hardware revision) (P30 or P33) + * LED 4: bit 5 (P35) + Notes: + * Set logical 0 to switch LED on; logical 1 to switch LED off. + * Bit 4 should be maintained at 1 to keep RF field on. + + Progressive field activation: + The ASK LoGO hardware can progressively power-up the antenna. + To use this feature we have to switch on the field by switching on + the field on PN533 (RFConfiguration) then set P34 to '1', and cut-off the + field by switching off the field on PN533 then set P34 to '0'. + */ + + /* Set P30, P31, P33, P35 to logic 1 and P32, P34 to 0 logic */ + /* ie. Switch LED1 on and turn off progressive field */ + pn53x_write_register(pnd, PN53X_SFR_P3, 0xFF, _BV(P30) | _BV(P31) | _BV(P33) | _BV(P35)); + } + if (DRIVER_DATA(pnd)->possibly_corrupted_usbdesc) + pn533_fix_usbdesc(pnd); + + return NFC_SUCCESS; } static int -pn53x_usb_set_property_bool(nfc_device *pnd, const nfc_property property, const bool bEnable) { - int res = 0; - if ((res = pn53x_set_property_bool(pnd, property, bEnable)) < 0) - return res; - - switch (DRIVER_DATA(pnd)->model) { - case ASK_LOGO: - if (NP_ACTIVATE_FIELD == property) { - /* Switch on/off LED2 and Progressive Field GPIO according to ACTIVATE_FIELD option */ - log_put(LOG_GROUP, - LOG_CATEGORY, - NFC_LOG_PRIORITY_DEBUG, - "Switch progressive field %s", - bEnable ? "On" : "Off"); - if (pn53x_write_register(pnd, PN53X_SFR_P3, _BV(P31) | _BV(P34), bEnable ? _BV(P34) : _BV(P31)) < 0) - return NFC_ECHIP; - } - break; - case SCM_SCL3711: - case SCM_SCL3712: - if (NP_ACTIVATE_FIELD == property) { - // Switch on/off LED according to ACTIVATE_FIELD option - if ((res = pn53x_write_register(pnd, PN53X_SFR_P3, _BV(P32), bEnable ? 0 : _BV(P32))) < 0) - return res; - } - break; - case NXP_PN531: - case NXP_PN533: - case SONY_PN531: - case SONY_RCS360: - case UNKNOWN: - // Nothing to do. - break; - } - return NFC_SUCCESS; +pn53x_usb_set_property_bool(nfc_device *pnd, const nfc_property property, const bool bEnable) +{ + int res = 0; + if ((res = pn53x_set_property_bool(pnd, property, bEnable)) < 0) + return res; + + switch (DRIVER_DATA(pnd)->model) { + case ASK_LOGO: + if (NP_ACTIVATE_FIELD == property) { + /* Switch on/off LED2 and Progressive Field GPIO according to ACTIVATE_FIELD option */ + log_put(LOG_GROUP, + LOG_CATEGORY, + NFC_LOG_PRIORITY_DEBUG, + "Switch progressive field %s", + bEnable ? "On" : "Off"); + if (pn53x_write_register(pnd, PN53X_SFR_P3, _BV(P31) | _BV(P34), bEnable ? _BV(P34) : _BV(P31)) < 0) + return NFC_ECHIP; + } + break; + case SCM_SCL3711: + case SCM_SCL3712: + if (NP_ACTIVATE_FIELD == property) { + // Switch on/off LED according to ACTIVATE_FIELD option + if ((res = pn53x_write_register(pnd, PN53X_SFR_P3, _BV(P32), bEnable ? 0 : _BV(P32))) < 0) + return res; + } + break; + case NXP_PN531: + case NXP_PN533: + case SONY_PN531: + case SONY_RCS360: + case UNKNOWN: + // Nothing to do. + break; + } + return NFC_SUCCESS; } static int -pn53x_usb_abort_command(nfc_device *pnd) { - DRIVER_DATA(pnd)->abort_flag = true; - return NFC_SUCCESS; +pn53x_usb_abort_command(nfc_device *pnd) +{ + DRIVER_DATA(pnd)->abort_flag = true; + return NFC_SUCCESS; } static int pn53x_usb_get_supported_modulation(nfc_device *pnd, const nfc_mode mode, - const nfc_modulation_type **const supported_mt) { - if ((DRIVER_DATA(pnd)->model != ASK_LOGO) || (mode != N_TARGET)) - return pn53x_get_supported_modulation(pnd, mode, supported_mt); - else // ASK_LOGO has no N_TARGET support - *supported_mt = no_target_support; - return NFC_SUCCESS; + const nfc_modulation_type **const supported_mt) +{ + if ((DRIVER_DATA(pnd)->model != ASK_LOGO) || (mode != N_TARGET)) + return pn53x_get_supported_modulation(pnd, mode, supported_mt); + else // ASK_LOGO has no N_TARGET support + *supported_mt = no_target_support; + return NFC_SUCCESS; } const struct pn53x_io pn53x_usb_io = { - .send = pn53x_usb_send, - .receive = pn53x_usb_receive, + .send = pn53x_usb_send, + .receive = pn53x_usb_receive, }; const struct nfc_driver pn53x_usb_driver = { - .name = PN53X_USB_DRIVER_NAME, - .scan_type = NOT_INTRUSIVE, - .scan = pn53x_usb_scan, - .open = pn53x_usb_open, - .close = pn53x_usb_close, - .strerror = pn53x_strerror, - - .initiator_init = pn53x_initiator_init, - .initiator_init_secure_element = NULL, // No secure-element support - .initiator_select_passive_target = pn53x_initiator_select_passive_target, - .initiator_poll_target = pn53x_initiator_poll_target, - .initiator_select_dep_target = pn53x_initiator_select_dep_target, - .initiator_deselect_target = pn53x_initiator_deselect_target, - .initiator_transceive_bytes = pn53x_initiator_transceive_bytes, - .initiator_transceive_bits = pn53x_initiator_transceive_bits, - .initiator_transceive_bytes_timed = pn53x_initiator_transceive_bytes_timed, - .initiator_transceive_bits_timed = pn53x_initiator_transceive_bits_timed, - .initiator_target_is_present = pn53x_initiator_target_is_present, - - .target_init = pn53x_target_init, - .target_send_bytes = pn53x_target_send_bytes, - .target_receive_bytes = pn53x_target_receive_bytes, - .target_send_bits = pn53x_target_send_bits, - .target_receive_bits = pn53x_target_receive_bits, - - .device_set_property_bool = pn53x_usb_set_property_bool, - .device_set_property_int = pn53x_set_property_int, - .get_supported_modulation = pn53x_usb_get_supported_modulation, - .get_supported_baud_rate = pn53x_get_supported_baud_rate, - .device_get_information_about = pn53x_get_information_about, - - .abort_command = pn53x_usb_abort_command, - .idle = pn53x_idle, - .powerdown = pn53x_PowerDown, + .name = PN53X_USB_DRIVER_NAME, + .scan_type = NOT_INTRUSIVE, + .scan = pn53x_usb_scan, + .open = pn53x_usb_open, + .close = pn53x_usb_close, + .strerror = pn53x_strerror, + + .initiator_init = pn53x_initiator_init, + .initiator_init_secure_element = NULL, // No secure-element support + .initiator_select_passive_target = pn53x_initiator_select_passive_target, + .initiator_poll_target = pn53x_initiator_poll_target, + .initiator_select_dep_target = pn53x_initiator_select_dep_target, + .initiator_deselect_target = pn53x_initiator_deselect_target, + .initiator_transceive_bytes = pn53x_initiator_transceive_bytes, + .initiator_transceive_bits = pn53x_initiator_transceive_bits, + .initiator_transceive_bytes_timed = pn53x_initiator_transceive_bytes_timed, + .initiator_transceive_bits_timed = pn53x_initiator_transceive_bits_timed, + .initiator_target_is_present = pn53x_initiator_target_is_present, + + .target_init = pn53x_target_init, + .target_send_bytes = pn53x_target_send_bytes, + .target_receive_bytes = pn53x_target_receive_bytes, + .target_send_bits = pn53x_target_send_bits, + .target_receive_bits = pn53x_target_receive_bits, + + .device_set_property_bool = pn53x_usb_set_property_bool, + .device_set_property_int = pn53x_set_property_int, + .get_supported_modulation = pn53x_usb_get_supported_modulation, + .get_supported_baud_rate = pn53x_get_supported_baud_rate, + .device_get_information_about = pn53x_get_information_about, + + .abort_command = pn53x_usb_abort_command, + .idle = pn53x_idle, + .powerdown = pn53x_PowerDown, }; diff --git a/utils/nfc-list.c b/utils/nfc-list.c index 53dcf615..bb6aaef9 100644 --- a/utils/nfc-list.c +++ b/utils/nfc-list.c @@ -113,7 +113,7 @@ main(int argc, const char *argv[]) exit(EXIT_FAILURE); } // Force TypeB for all derivatives of B - if (mask & 0xf0) + if (mask & 0xd0) mask |= 0x08; } else { ERR("%s is not supported option.", argv[arg]);