Hi There,
We are using LPC4337 MCUs in multiple projects. Two of which require a USB port to enumerate as a USB Mass Storage Device with and SD/MMC card as the backing store.
We have it working using the ROM drivers however writing large files fails consistently at the 3G boundary. Specifically at 3040.3MB. The SD card is formatted with FAT32 however the failure also occurs when the card is formatted with ExFAT as well.
Looking at the Linux logs at the time of the failure we see the following:
sd 8:0:0:0: [sdc] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE cmd_age=0s sd 8:0:0:0: [sdc] tag#0 Sense Key : Not Ready [current] sd 8:0:0:0: [sdc] tag#0 Add. Sense: Cannot read medium - unknown format sd 8:0:0:0: [sdc] tag#0 CDB: Read(10) 28 00 00 5a cb ff 00 00 08 00 print_req_error: 4119 callbacks suppressed blk_update_request: I/O error, dev sdc, sector 5950463 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
This appears to be an issue with the NXP USB stack. Sure enough, digging through the code reveals the following obscure comment:
if (USBD_API->version > 0x01111101) { /* New ROM stack version */ msc_param.MemorySize64 = sdcardinfo.card_info.device_size; msc_param.MemorySize = 0; } else { /* Old ROM stack version - cannot support more than 4GB of memory card */ msc_param.MemorySize = sdcardinfo.card_info.device_size; }
The Old ROM stack path is the code that gets executed.
The state of NXP LPC USB support is confusing to say the least.
Any answers to the questions above would be welcomed. Especially if there is a link to a USBD library archive that is a later version than 0x01111101.
Regards,
Mike
Hi Mike,
I found a few things in an older thread, so that's just a quick info shot. But maybe it answers already some of your questions. I have some legacy knowledge on this platform, that's why I jumped in.
###############
The USB ROM code for the mass storage class in devices with bootcode version older than 11.3 does indeed limit the size of partitions to 4 GiB. This is due to the 32-bit MemorySize field as you have pointed out. Also callback functions use 32-bit offsets only.
The "USBD" code has been updated to support larger partitions, and the 64-bit MemorySize64 replaces the old 32-bit MemorySize. The header file in LPCOpen reflects this.
The MemorySize parameter should be defined as 0 and then the MemorySize64 must be set to the actual size
Note that callback functions will take an additional parameter "high_offset" for the upper 32-bit of the 64-bit offset.
Unfortunately, devices with older ROM code don't support MemorySize64. This affects all flash based LPC1800 and LPC4300 devices, and older flashless devices in these two series. Only the latest flashless LPC18x0 and LPC43x0 have 64-bit support in the mass storage class implementation from boot code version 11.3 onwards.
Devices for which USBD exists as a library in LPCOpen (e.g. LPC17xx) support large disk sizes. However, a USBD library for LPC1800/LPC4300 is not available.
################
Regards,
Bernhard.
Hi Bernhard,
Thanks for your reply. I had figured out most of those details and I also found the USBD source (including the original ROM USBD source) in https://www.nxp.com/docs/en/application-note/TN00041.zip
This is indeed the USBD code for the LPC18xx/43xx MCU.
I managed to piece all that together and rework my code to use that stack and got it up and running.
The version identifier (from mw_usbd_rom_api.c) of the the code in TN00041.zip is 0x02233405. Much later than the version of the ROM stack it appears.
It does indeed have the changes for 64 bit byte offset value in the control structure but I don't think anyone ever tested this code by writing more than 4G of data to a device. Mainly because it fails with the file system being corrupted.
There is a bug in this version as well and as soon as you reach the 4G boundary the offset wraps back to zero destroying the FAT first followed by critical structures at the beginning of the cluster heap. This destroys the filesystem (exfat in my case) and the partition cannot be mounted after that. The parameter high_offset is always passed in as zero.
After spending a few hours wandering through code with a little bit of wiresharking and debugging I found the issue and fixed it. While the offset in the USB control structure is a uint64_t, the variable that is used to extract the LBA value from the USB packet is a uint32_t which causes 32 bit arithmetic to be used erroneously.
The function mwMSC_RWSetup in mw_usbd_mscuser.c contains the following line that converts the LBA address to the 64 bit offset:
pMscCtrl->Offset = n * pMscCtrl->BlockSize;
This line needs a cast to make sure that 64 bit arithmetic is used and should read:
pMscCtrl->Offset = (uint64_t)n * pMscCtrl->BlockSize;
With that fix in place I was able to write multiple 7GB files to the sd card with out any problem.
Hopefully this post helps 2^32+1 people.
Best regards,
Mike