Fix USB descriptors in SCL3711, NXP_PN533 and ASK LoGO if needed when closing device
The problem occurs in the following succession of events: * Emit commands larger than 17 bytes * Re-enumerate USB devices without power cycle, e.g. a warm reboot of the PC The bug can be reproduced for testing purposes with usbreset.c from https://askubuntu.com/questions/645/how-do-you-reset-a-usb-device-from-the-command-line#661 $ lsusb|grep NFC Bus 001 Device 010: ID 04e6:5591 SCM Microsystems, Inc. SCL3711-NFC&RW $ sudo ./usbreset /dev/bus/usb/001/010 Resetting USB device /dev/bus/usb/001/010 Reset successful $ echo 06000000000000000000000000000000000000 |pn53x-tamashell $ sudo ./usbreset /dev/bus/usb/001/010 Resetting USB device /dev/bus/usb/001/010 Error in ioctl: No such device $ lsusb|grep NFC ... device disappeared
This commit is contained in:
parent
c958b2c25d
commit
38164c49ef
1 changed files with 107 additions and 7 deletions
|
@ -5,7 +5,7 @@
|
|||
* Copyright (C) 2009 Roel Verdult
|
||||
* Copyright (C) 2009-2013 Romuald Conty
|
||||
* Copyright (C) 2010-2012 Romain Tartière
|
||||
* Copyright (C) 2010-2013 Philippe Teuwen
|
||||
* Copyright (C) 2010-2017 Philippe Teuwen
|
||||
* Copyright (C) 2012-2013 Ludovic Rousseau
|
||||
* See AUTHORS file for a more comprehensive list of contributors.
|
||||
* Additional contributors of this file:
|
||||
|
@ -81,6 +81,7 @@ struct pn53x_usb_data {
|
|||
uint32_t uiEndPointOut;
|
||||
uint32_t uiMaxPacketSize;
|
||||
volatile bool abort_flag;
|
||||
bool possibly_corrupted_usbdesc;
|
||||
};
|
||||
|
||||
// Internal io struct
|
||||
|
@ -136,10 +137,101 @@ const struct pn53x_usb_supported_device pn53x_usb_supported_devices[] = {
|
|||
{ 0x04E6, 0x5591, SCM_SCL3711, "SCM Micro / SCL3711-NFC&RW", 0x04, 0x84, 40 },
|
||||
{ 0x04E6, 0x5594, SCM_SCL3712, "SCM Micro / SCL3712-NFC&RW", 0, 0, 0 },
|
||||
{ 0x054c, 0x0193, SONY_PN531, "Sony / PN531", 0, 0, 0 },
|
||||
{ 0x1FD3, 0x0608, ASK_LOGO, "ASK / LoGO", 0, 0, 0 },
|
||||
{ 0x1FD3, 0x0608, ASK_LOGO, "ASK / LoGO", 0x04, 0x84, 40 },
|
||||
{ 0x054C, 0x02E1, SONY_RCS360, "Sony / FeliCa S360 [PaSoRi]", 0, 0, 0 }
|
||||
};
|
||||
|
||||
// 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,
|
||||
};
|
||||
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,
|
||||
};
|
||||
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,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
if (szXramUsbDesc == 0)
|
||||
return;
|
||||
/*
|
||||
// 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++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Restore USB descriptors
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "%s", "Fixing USB descriptors corruption");
|
||||
// Don't write more regs at once or it will trigger the bug and corrupt what we're busy reading!
|
||||
uint8_t abtCmdWR[] = { WriteRegister, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
size_t szCmdWR = sizeof(abtCmdWR);
|
||||
uint8_t nWRreg = ((sizeof(abtCmdWR) - 1) / 3);
|
||||
uint8_t abtRxWR[1];
|
||||
for (uint8_t i = 0x19, j = 0; i < 0x19 + szXramUsbDesc;) {
|
||||
if (j == szXramUsbDesc)
|
||||
break;
|
||||
for (uint8_t k = 0; (k < nWRreg) && (j < szXramUsbDesc); k++) {
|
||||
abtCmdWR[(3 * k) + 2] = i++;
|
||||
abtCmdWR[(3 * k) + 3] = btXramUsbDesc[j++];
|
||||
}
|
||||
if (j == szXramUsbDesc)
|
||||
szCmdWR = ((((szXramUsbDesc - 1) % nWRreg) + 1) * 3) + 1;
|
||||
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)
|
||||
{
|
||||
|
@ -316,6 +408,7 @@ pn53x_usb_open(const nfc_context *context, const nfc_connstring connstring)
|
|||
.pudh = NULL,
|
||||
.uiEndPointIn = 0,
|
||||
.uiEndPointOut = 0,
|
||||
.possibly_corrupted_usbdesc = false,
|
||||
};
|
||||
struct usb_bus *bus;
|
||||
struct usb_device *dev;
|
||||
|
@ -338,10 +431,12 @@ pn53x_usb_open(const nfc_context *context, const nfc_connstring connstring)
|
|||
if ((data.pudh = usb_open(dev)) == NULL)
|
||||
continue;
|
||||
// Retrieve end points, using default if dev->config is broken
|
||||
if (dev->config == NULL)
|
||||
if (dev->config == NULL) {
|
||||
pn53x_usb_get_end_points_default(dev, &data);
|
||||
else
|
||||
data.possibly_corrupted_usbdesc = true;
|
||||
} else {
|
||||
pn53x_usb_get_end_points(dev, &data);
|
||||
}
|
||||
// Set configuration
|
||||
int res = usb_set_configuration(data.pudh, 1);
|
||||
if (res < 0) {
|
||||
|
@ -443,6 +538,9 @@ pn53x_usb_close(nfc_device *pnd)
|
|||
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);
|
||||
|
||||
pn53x_idle(pnd);
|
||||
|
||||
int res;
|
||||
|
@ -471,6 +569,7 @@ pn53x_usb_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, con
|
|||
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;
|
||||
|
@ -490,8 +589,8 @@ pn53x_usb_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, con
|
|||
// 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 nextly executed function (ie.
|
||||
// pn53x_usb_receive()) will be able to retreive the correct response
|
||||
// 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) {
|
||||
|
@ -501,7 +600,6 @@ pn53x_usb_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, con
|
|||
return pnd->last_error;
|
||||
}
|
||||
}
|
||||
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -702,6 +800,8 @@ pn53x_usb_init(nfc_device *pnd)
|
|||
/* 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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue