USB MSC Host Issues.

Discussion created by lpcware Employee on Jun 15, 2016
Content originally posted in LPCWare by MikeSimmonds on Thu Mar 26 06:04:17 MST 2015
Hi forumites.

I am in the process of developing mass storage class host software on the LPC1778.

Of course, the UM bows out and refers me to the OHCI spec. [Note to NXP, a link to this in the UM would be nice in the next revision.]
Any way, I sourced this and ...

I have completed the basic USB detatch/insert and enumeration pass with no worries.
When I find a USB stick with a suitable SCSI/Bulk Only Configuation, I move on to the MSC part of things.

I found some 2008 NXP CMSIS based source [CodeRed era?] to look at.

Their Init function

    GetMaxLUN();                        /* Get maximum logical unit number   */
    for (retry  = 80; retry--)
        rc = TestUnitReady();           /* Test whether the unit is ready    */
        if (rc == OK)
        GetSenseInfo();                 /* Get sense information             */
    if (rc != OK)
    rc = ReadCapacity(numBlks, blkSize);/* Read capacity of the disk         */
    BlkSize = *blkSize;// Set global
    rc = Inquire(inquiryResult);
    return (rc);

My 'InitMS' function followed this scheme but omited 'GetLUN' because the any result was not available and never used.
Initially, I was getting hangs because the 1st TestUnitReady was returning continuous NAK's and the 'TDProcess' subroutine
was spinning on 'WDH Done' (as recognised by the USB interrupt).
But for NAK returns, this never happens -- Is this a flaw in the OHCI design?

So I had some fun and games trying to set a time out that was long enough for a 'done' (OK/Non-NAK error) to be set.

Any way, I ended up (due to web postings) moving the Device Enquiry before the TestUnit ready.

Up to this point, I was testing with a single USB stick (in case I bricked it).
When I stared testing on 8 other USB sticks that I had to hand, some anomalies arose.

One would show flickering of the USB connect LED for about 17 seconds and thereafter enumerate and work fine.
One would fail (with NAKS) if inserted into a running system, but succeded if I use the debugger to set the PC back to the enumeration (without removing the drive).
One simply refused to respond to Enquiry either way.

Anyway, I found a solution of sorts, but I dont understand why.

I added DeviceReset EP1, EP2 clear feature before the 1st Enquiry, but these all return STALL, So ignore them.

The first MSC call is Enquiry -- if I add a delay (and this is the stange part) between the CBW transaction and the data in phase
it all works on all devices and I can read the 1st sector at LBA 0. [No other SCSI commands have such a delay.]
If I don't use the delay, when things fail, absolutely everything just returns endless NAKs. Even with (10's of seconds timeouts).

However, for the worst case (of the devices that I have available) I need a delay of over 800 milliseconds for reliable operation.
[Kingston DT101 G2, 16GB]

Does anyone have any comments, observations, experience, or anecdotes regarding this.

As I said, I think now that I can move forward and work on the file system code.

Code Snips

; InitMS!

fn InitMS

push{r4-r7,lr}; save regs
ldrr5, =usbBase; get base register
movsr7, hostBase; and host base

ldrr1, =bulkReset; reset device
ldrr1, =clear1; clear endpoint 1
ldrr1, =clear2; clear endpoint 2

;;blMaxLUN; why -- no result, not used
blDevEnquiry; why -- result not used
cbzr0, 1f;
orrsr0, 0x810;

1:movsr12, 256; timeout
0:blTestUnitReady; wait for device ready
cbzr0, 1f; ok
blRequestSense; keep in phase?
subsr12, 1; don't wait too long
orrsr0, 0x820;

1:blGetCapacity; get size info
cbzr0, 9f; oops
orrsr0, 0x830;

9:pop{r4-r7,pc}; return

fe InitMS

; DevEnquiry!

fn DevEnquiry

push{lr}; save regs
movsr0, cmdEnquiry; setup

movsr0, pidOut; send command wrapper
movsr1, cbwSz;
movsr2, 0; use tBuff
cbnzr0, 9f; oops

; for some devices, a delay here seems to be essential
; one device needs approx 800 ms
; It might also be reqd for the 1st SCSI operation.

movsr0, 0x380; delay for a bit, but why? !!!

movsr0, pidIn; get payload
movsr1, 36; enquiry data size
movsr2, 0; use tBuff
cbnzr0, 9f; oops

addsr0, r7, devEnq; stash details
addsr1, r7, tBuff;
movsr2, 36;

movsr2, 42; only reason for doing this

movsr0, pidIn; get status
movsr1, cswSz;
movsr2, 0; use tBuff
cbnzr0, 9f; oops
ldrbr0, [r7, tBuff+12]; get status code (0 = ok)


fe DevEnquiry

Cheers, Mike