LUFA Mass Storage on AVR

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've made the title somewhat generic so that others might use the thread in case my question gets lost in the noise or too detailed for anyone to care. In my specific case I'm using

- at90usb1286 running at 16MHz (ext clock)

- u-SD card (SDHC 8GB) connected trough SPI.

- AS7

- Windows 10

- JTAG programmer

- LUFA 151115

- Chan's fatfs r0.12b (http://elm-chan.org/fsw/ff/00ind...)

 

I downloaded a LUFA demo project for dataflash from within AS7, using Tools->Extensions and updates, then download LUFA. After that I did File->New->Example project. Selected FourWalledCubicle,  Mass storage Host Demo (Class driver APIs) AVR8 architecture.

 

My real project is in another AS7 setup, so I then simply copied the files to my project together with the defined gcc symbols (Project-properties-Toolchain-AVR Gnu C compiler-Symbols).

 

After some initial gotcha's (disable all LED driving is a good thing if your platform is already connected to those io:s) I get happy Windows sound when I connect but, at best, an empty drive (0 byte sized).

I can see that Windows requests (and gets) the size. But it looks like it stalls after reading 1 block at sector 0 (1 block is 16 bytes here if I understood sdcardmanager right). Not that the SDcardmanager locks up or anything, the read function exits ok and I see the sector information in the buffers. But Windows is not happy. It looks like nothing happens for a while, then it resets the connection.

Anybody else seen this?

 

This topic has a solution.

---

 

Last Edited: Wed. Nov 23, 2016 - 11:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

74hct04 wrote:
But Windows is not happy. It looks like nothing happens for a while, then it resets the connection.
Windows has USB debugging tools that were updated for Windows 8 (USB 3 was added).

Microsoft

Microsoft

Windows 10 hardware dev

Universal Serial Bus (USB)

https://msdn.microsoft.com/en-us/library/windows/hardware/ff538930(v=vs.85).aspx

...

USB-related videos

...

USB Debugging Innovations in Windows 8 ...

...

 

https://msdn.microsoft.com/en-us/library/windows/hardware/ff538820(v=vs.85).aspx (USB device class drivers included in Windows)

https://msdn.microsoft.com/en-us/library/windows/hardware/ff560019 (USBView)

 

Edit : USBView

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Tue. Nov 22, 2016 - 04:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I downloaded USBview and got the information below.

I have also attached a picture from USBlyzer, where you can see the flow

- Reset port

- Test unit ready

- Read(SCSI 10) sector 0, 1 block

- Strange things happens...

- Reset port

- Test unit ready

... and so on. Ideas? LUFA not working on at90usb1286 + Windows 10? In desperation I tried with a 2GB card but obviously it didn't work.

[Port1]  :  USB-masslagringsenhet

Is Port User Connectable:         yes
Is Port Debug Capable:            no
Companion Port Number:            0
Companion Hub Symbolic Link Name:
Protocols Supported:
 USB 1.1:                         yes
 USB 2.0:                         yes
 USB 3.0:                         no

Device Power State:               PowerDeviceD0

       ---===>Device Information<===---
English product name: "LUFA Mass Storage Demo"

ConnectionStatus:                  
Current Config Value:              0x01  -> Device Bus Speed: Full (is not SuperSpeed or higher capable)
Device Address:                    0x05
Open Pipes:                           2

          ===>Device Descriptor<===
bLength:                           0x12
bDescriptorType:                   0x01
bcdUSB:                          0x0110
bDeviceClass:                      0x00  -> This is an Interface Class Defined Device
bDeviceSubClass:                   0x00
bDeviceProtocol:                   0x00
bMaxPacketSize0:                   0x08 = (8) Bytes
idVendor:                        0x03EB = Atmel Corporation
idProduct:                       0x2045
bcdDevice:                       0x0001
iManufacturer:                     0x01
     English (United States)  "Dean Camera"
iProduct:                          0x02
     English (United States)  "LUFA Mass Storage Demo"
iSerialNumber:                     0xDC
     English (United States)  "85531343339351916071"
bNumConfigurations:                0x01

          ---===>Open Pipes<===---

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x83  -> Direction: IN - EndpointID: 3
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x05

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x04  -> Direction: OUT - EndpointID: 4
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x05

       ---===>Full Configuration Descriptor<===---

          ===>Configuration Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x02
wTotalLength:                    0x0020  -> Validated
bNumInterfaces:                    0x01
bConfigurationValue:               0x01
iConfiguration:                    0x00
bmAttributes:                      0x80  -> Bus Powered
MaxPower:                          0x32 = 100 mA

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x00
bAlternateSetting:                 0x00
bNumEndpoints:                     0x02
bInterfaceClass:                   0x08  -> This is a Mass Storage USB Device Interface Class
bInterfaceSubClass:                0x06
bInterfaceProtocol:                0x50
iInterface:                        0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x83  -> Direction: IN - EndpointID: 3
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x05

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x04  -> Direction: OUT - EndpointID: 4
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x05

 

Attachment(s): 

---

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have rewritten

static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo)
{
    uint32_t LastBlockAddressInLUN = (LUN_MEDIA_BLOCKS - 1);
    uint32_t MediaBlockSize        = VIRTUAL_MEMORY_BLOCK_SIZE;

 

    Endpoint_Write_Stream_BE(&LastBlockAddressInLUN, sizeof(LastBlockAddressInLUN), NULL);
    Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NULL);
    Endpoint_ClearIN();

 

    /* Succeed the command and update the bytes transferred counter */
    MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8;

    return true;
}

... to:

static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo)
{
    uint32_t LastBlockAddressInLUN = (LUN_MEDIA_BLOCKS - 1);
    uint32_t MediaBlockSize        = VIRTUAL_MEMORY_BLOCK_SIZE;

 

    volatile DRESULT r;
    r=mmc_disk_ioctl(GET_SECTOR_COUNT,&LastBlockAddressInLUN);
    LastBlockAddressInLUN--;

    Endpoint_Write_Stream_BE(&LastBlockAddressInLUN, sizeof(LastBlockAddressInLUN), NULL);
    Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NULL);
    Endpoint_ClearIN();

    /* Succeed the command and update the bytes transferred counter */
    MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8;

    return true;
}

 

I assume it's (LUN_MEDIA_BLOCKS - 1) in the original coed because it asks for the last block address (which is, I guessed, the 0 based number for the last 512-byte sector on the disk).

Correct?

 

The reason I ask is that if I set LastBlockAddressInLUN=0, after calling ioctl, D: pops up at once with a requester saying (not surprisingly) "D: is not available, wrong function" (or similar, translated).

If I set it to e.g 1024 (or anything) I basically get the same behavior as always. Windows tries to SCSI_CMD_READ_10 (0x28), then a pause of 15s, then a SCSI_CMD_TEST_UNIT_READY (0x0) where after it starts repeating back at 0x28.

 

---

 

Last Edited: Wed. Nov 23, 2016 - 07:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did another test:

Started up a new AS7 project, loaded the LOWLEVEL_MASS_STORAGE LUFA example, removed

 DataflashManager_ResetDataflashProtections()

 DataflashManager_CheckDataflashOperation() (returns true)

Changed

 DataflashManager_ReadBlocks() always reads 0

 DataflashManager_WriteBlocks() do nothing

 SCSI_Command_Read_Capacity_10() returns 1023 blocks.

 

...and I get the exact same behavior as in my project.

 

I have noticed that the first read returns all 0 in my project. The first sector (MBR) contains zeroes during the first 16 bytes (the "block" size in  DataflashManager_ReadBlocks seem to be 16 bytes). Looking at the SD card map here:

http://ugweb.cs.ualberta.ca/~c27...SDCardMap.pdf

...shows that the interesting stuff at the boot sector actually starts at 0x1BF (the sector is 0x200 =512 bytes). So why does Windows read the first 16 bytes and then nothing?

 

Yet another test:

I dug up an old STK525, with a AT45DB321C (SPI, 32Mbit). I used one of the AS7 MS example projects (AVR8, class drivers). Added Drivers/Misc and Drivers/Peripherals, as well as Board/AVR8/STK525 (had to download the zip file from LUFA web page for these).

Changed BOARD_TYPE to BOARD_STK525, F_CPU and F_USB = 8000000, recompiled and flashed using JTAG. This works.

 

 

---

 

Last Edited: Wed. Nov 23, 2016 - 08:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

USB Mass Storage Device blocks are always 512 bytes, not 16 bytes. Can you try reading out 512 bytes' worth of data for each requested LUN block?

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Below is my draft read function, hooking in to Chan's io functions (which works) for the FAT file system.

Basically I read a 512-byte sector when needed and pick out the 16-byte chunks from it. Maybe I got it wrong somewhere.

volatile uint8_t sd_sector_data[512];

void SDCardManager_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const uint32_t BlockAddress,
uint16_t TotalBlocks)
{
    uint8_t i=0;
    uint32_t sector;
    uint16_t sector_offset,ix;
    volatile DRESULT r;

    sector=BlockAddress>>4;                    // Assume blockaddress is address of 16-byte block.
    sector_offset=BlockAddress & 0xf;        // Offset into a 512-byte sector.

    if ((!sector_is_cached) || (cached_sector!=sector)) {
        // If the sector is not already read:
        r= mmc_disk_read ((BYTE *)sd_sector_data,sector,1);    // Read one sector (512 bytes)
        if (r!=RES_OK) return; // Get out. TODO: error handling
        cached_sector=sector;
        sector_is_cached=1;
    }

    /* Wait until endpoint is ready before continuing */
    if (Endpoint_WaitUntilReady())    return;

    ix=sector_offset;        // Start reading at this position in the cached sector
    
    while (TotalBlocks)
    {
        uint8_t BytesInBlockDiv16 = 0;

        /* Read an endpoint packet sized data block from the Dataflash */
        while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4))
        {
            /* Check if the endpoint is currently full */
            if (!(Endpoint_IsReadWriteAllowed()))
            {
                /* Clear the endpoint bank to send its contents to the host */
                Endpoint_ClearIN();

                /* Wait until the endpoint is ready for more data */
                if (Endpoint_WaitUntilReady())
                return;
            }

            /* Read one 16-byte chunk of data from the Dataflash */
            for (i=0;i<16;i++)
            {
                if(ix>=512){        // If we have passed the sector boundry
                    ix=0;            // Start at beginning of next sector
                    sector++;        // Move to next sector
                    r= mmc_disk_read ((BYTE *)sd_sector_data,sector,1);    // Read one sector (512 bytes)
                    if (r!=RES_OK) return; // Get out. TODO: error handling
                    cached_sector=sector;
                }
                Endpoint_Write_8(sd_sector_data[ix++]);
            }

            /* Increment the block 16 byte block counter */
            BytesInBlockDiv16++;

            /* Check if the current command is being aborted by the host */
            if (MSInterfaceInfo->State.IsMassStoreReset)
            return;
        }

        /* Decrement the blocks remaining counter */
        TotalBlocks--;
    }

    /* If the endpoint is full, send its contents to the host */
    if (!(Endpoint_IsReadWriteAllowed()))
    Endpoint_ClearIN();

}

 

The last result now is that Windows (after some time) wants to format the drive, just as it wants when it gets all 0:s returned. Size is correct in disk manager. 

Moving the SD card to a reader I see it's already formatted (and has a file on it) so I'm missing something big here.

EDIT:

Found a bug: moved ix=sector_offset; to above the while(), now Windows says "Insert a disk in drive D:". Windows is never happy. But getting happier it seems.

 

---

 

Last Edited: Wed. Nov 23, 2016 - 11:04 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, now I understand. BlockAddress is actually the 512 byte sector number. The code works with this (a bit unoptimized but at least it works):

void SDCardManager_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, const uint32_t BlockAddress, uint16_t TotalBlocks)
{
    uint8_t i=0;
    uint32_t sector;
    uint16_t ix;
    DRESULT r;

    // 512 byte blocks in BlockAddress:
    sector=BlockAddress;                    // Assume blockaddress is address of 512-byte block.

    if ((!sector_is_cached) || (cached_sector!=sector)) {
        // If the sector is not already read:
        r= mmc_disk_read ((BYTE *)sd_sector_data,sector,1);    // Read one sector (512 bytes)
        if (r!=RES_OK) return; // Get out. TODO: error handling
        cached_sector=sector;
        sector_is_cached=1;
    }

    /* Wait until endpoint is ready before continuing */
    if (Endpoint_WaitUntilReady())    return;

 

    ix=0;        // Start reading at this position in the cached sector
    while (TotalBlocks)
    {
        uint8_t BytesInBlockDiv16 = 0;

        /* Read an endpoint packet sized data block from the SD */
        while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4))
        {
            /* Check if the endpoint is currently full */
            if (!(Endpoint_IsReadWriteAllowed()))
            {
                /* Clear the endpoint bank to send its contents to the host */
                Endpoint_ClearIN();

                /* Wait until the endpoint is ready for more data */
                if (Endpoint_WaitUntilReady())    return;
            }

            /* Read one 16-byte chunk of data from the SD, send it byte-by byte to the host */
            for (i=0;i<16;i++)
            {
                if(ix>=512){        // If we have passed the sector boundary
                    ix=0;            // Start at beginning of next sector
                    sector++;        // Move to next sector
                    r= mmc_disk_read ((BYTE *)sd_sector_data,sector,1);    // Read one sector (512 bytes)
                    if (r!=RES_OK) return; // Get out. TODO: error handling
                    cached_sector=sector;
                }
                Endpoint_Write_8(sd_sector_data[ix++]);
            }

            /* Increment the block 16 byte block counter */
            BytesInBlockDiv16++;

            /* Check if the current command is being aborted by the host */
            if (MSInterfaceInfo->State.IsMassStoreReset)
            return;
        }

        /* Decrement the blocks remaining counter */
        TotalBlocks--;
    }

    /* If the endpoint is full, send its contents to the host */
    if (!(Endpoint_IsReadWriteAllowed()))
    Endpoint_ClearIN();

}

 

A big thank you for the hint. Now for the write function....

---

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For completeness, and in case it helps someone else, here is my write function:

void SDCardManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
                                  const uint32_t BlockAddress,
                                  uint16_t TotalBlocks)
{
    uint32_t sector;
    uint16_t ix;
    DRESULT r;

    // 512 byte blocks in BlockAddress:
    sector=BlockAddress;                    // Assume blockaddress is address of 512-byte sector.

    /* Wait until endpoint is ready before continuing */
    if (Endpoint_WaitUntilReady()) return;

    while (TotalBlocks)
    {
        uint8_t BytesInBlockDiv16 = 0;

        // Read data in chunks of 16 bytes from the host.
        // We write only complete 512-byte sectors.
        ix=0;
        while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4))
        {
            /* Check if the endpoint is currently empty */
            if (!(Endpoint_IsReadWriteAllowed()))
            {
                Endpoint_ClearOUT();                        // Clear the current endpoint bank
                if (Endpoint_WaitUntilReady())  return;        // Wait until the host has sent another packet, return on error
            }

            
            /* Read one 16-byte chunk of data from the host, write it to the sector buffer */
            for (uint8_t i=0;i<16;i++)
            {
                sd_sector_data[ix++]=Endpoint_Read_8();
            }

            /* Increment the block 16 byte block counter */
            BytesInBlockDiv16++;

            /* Check if the current command is being aborted by the host */
            if (MSInterfaceInfo->State.IsMassStoreReset) return;
        }

        r= mmc_disk_write ((BYTE *)sd_sector_data,sector,1);    // Write one sector (512 bytes)
        if (r!=RES_OK) return; // Get out
        sector++;

        /* Decrement the blocks remaining counter */
        TotalBlocks--;
    }

    /* If the endpoint is empty, clear it ready for the next packet from the host */
    if (!(Endpoint_IsReadWriteAllowed()))       Endpoint_ClearOUT();
}

 

---

 

Last Edited: Wed. Nov 23, 2016 - 01:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok it's not the fastest. I can live with that. But one annoying thing is that Windows takes its own little time to pop up the drive when the cable is inserted. Don't know what it's doing; I did put a LED on the SCSI function and it just blinks occasionally and after a long delay the drive shows up in Explorer. I'd expect it to spend a lot of time in the SCSI functions, but it seems to be doing something else.

 

It's like Windows is talking really slowly to the device, with pauses in between. Anyone got an idea what is going on? Have I missed some setting?

It can be like a minute before the drive shows.

---

 

Last Edited: Fri. Dec 2, 2016 - 09:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi !

What's up in your project ? I'm also working on this ^^

Could you optimize your code ?

Could you manage to speed up the transfers and the detection by Windows ?

 

Thanks :)