-
Notifications
You must be signed in to change notification settings - Fork 2.1k
usbus/msc: add initial support for Mass Storage Class #15941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
||
/* Internal buffer to handle size difference between MTD layer and USB | ||
endpoint size as some MTD implementation (like mtd_sdcard) doesn't allow | ||
endpoint size transfer */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean it does not support reading at an offset?
mtd_sdcard
might actually be the only driver that does not support that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes mtd_sdcard doesn't support the offset argument so I had to workaround this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mtd_mci
, which is also used to drive SD-cards, has an internal page buffer to support offsets.
I wonder if we should rather add that to mtd_sdcard
too (maybe as a module, so it can be left out if e.g. fatfs is used, that will only ever write pages and has it's own page buffer for that.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is a way to use the offset for sdcard, I would be happy to adapt the code.
Nevertheless, I think we should keep an internal buffer to store a page in memory for write operations to avoid multiple write (64 bytes) for a single page.
sys/usb/usbus/msc/scsi.c
Outdated
assert(mtd0->page_size <= HOST_MINIMAL_PAGE_SIZE); | ||
|
||
/* Host side expects to have at least 512 bytes per page, otherwise it will reject | ||
the setup. Thus add some logic here to be able to use smaller page size mtd device */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we report a larger page size to the host?
I suppose then the statically allocated buff
would be too small to act as a page cache.
(could we use malloc()
here if we need the page cache at all? 😇)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you report a larger page size to the host ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Internal flash can have 4k or 8k pages.
If we want to support that too we would have to report larger pages - unless we also want to map that to virtual 512 byte pages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reporting larger page size is somehow limited.
Linux rejects me when I was testing SAME54 flashpage because I reported 8192 bytes as page size.
So supported values are 512, 1024, 2048. (maybe 4096 I didn't try).
To give a little more details.
In a read/write command case:
Since our hardware only support USB full-speed. We are limited to 64 bytes for Bulk Endpoints by the spec and thus, we need to slice the page size into 64 bytes chunks. |
Pushed Kconfig support |
Finally got it working on Windows. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few things I noticed when skimming this for how tied it is to mtd (not that that tie would be a bad thing, was just curious).
tests/usbus_msc/Makefile
Outdated
|
||
ifeq ($(USB_VID):$(USB_PID), 1209:0001) | ||
$(shell $(COLOR_ECHO) "$(COLOR_RED)Private testing pid.codes USB VID/PID used!, do not use it outside of test environments!$(COLOR_RESET)" 1>&2) | ||
$(shell $(COLOR_ECHO) "$(COLOR_RED)MUST NOT be used on any device redistributed, sold or manufactured, VID/PID is not unique!$(COLOR_RESET)" 1>&2) | ||
endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hasn't been needed any more for quite some while (see #13382)
ifeq ($(USB_VID):$(USB_PID), 1209:0001) | |
$(shell $(COLOR_ECHO) "$(COLOR_RED)Private testing pid.codes USB VID/PID used!, do not use it outside of test environments!$(COLOR_RESET)" 1>&2) | |
$(shell $(COLOR_ECHO) "$(COLOR_RED)MUST NOT be used on any device redistributed, sold or manufactured, VID/PID is not unique!$(COLOR_RESET)" 1>&2) | |
endif |
tests/usbus_msc/Makefile
Outdated
USB_VID ?= 1209 | ||
USB_PID ?= 0001 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are predefined now:
USB_VID ?= 1209 | |
USB_PID ?= 0001 | |
USB_VID ?= $(USB_VID_TESTING) | |
USB_PID ?= $(USB_PID_TESTING) |
tests/usbus_msc/main.c
Outdated
#include <stdio.h> | ||
|
||
#include "shell.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Odd indentation
#include <stdio.h> | |
#include "shell.h" | |
#include <stdio.h> | |
#include "shell.h" |
Yes this PR is a bit staging and need a rebase. |
Revive this PR. Rebased to lastest master and squash at the same time. |
@OlegHahm Not sure I've understand your question. Right now this PR is only waiting some review and testing. |
Somehow an I tried it with my
Reading the external flash works:
|
WRITE operations */ | ||
uint16_t block_offset; /**< Internal offset for endpoint size chunk transfer */ | ||
uint8_t pages_per_vpage; /**< Number of pages to fill host expected page size */ | ||
} usbus_msc_device_t; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not add a mtd_dev_t *mtd
member here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's ongoing.
{ | ||
usbus_msc_device_t *msc = (usbus_msc_device_t*)handler; | ||
msc_read_fmt_capa_pkt_t *pkt = (msc_read_fmt_capa_pkt_t*)msc->in_buf; | ||
uint32_t blk_nb = (mtd0->sector_count * mtd0->pages_per_sector); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you can
uint32_t blk_nb = (mtd0->sector_count * mtd0->pages_per_sector); | |
uint32_t blk_nb = (msc->mtd->sector_count * msc->mtd->pages_per_sector); |
Is that a protocol limitation? HDDs with 4k sectors do exist for more then a decade now and Linux supports 4k native mode since 2.6.31, Windows since version 8 |
No, this was an implementation limitation. Currently the page size limitation can be found here |
Huh, I wasn't expecting to implement READ16 and WRITE16 operations any time soon due to poor performance. |
I think the problem was that my SD card was mtd2, but mtd0 is hard-coded so setting |
|
||
/* buffer is full, write it and point to new block if any */ | ||
if (msc->block_offset >= (MTD_MSC->page_size * msc->pages_per_vpage)) { | ||
mtd_write_page(MTD_MSC, msc->buffer, msc->block * msc->pages_per_vpage, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you write whole pages you might also use mtd_write_page_raw
.
Then you could even use the per-mtd device sector sized work buffer instead of allocating your own.
Alternatively you can deselect the mtd_pagewise module if you do raw writes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this approach is for devices with more than 4096 bytes as page size. I'll be force to report 4096 as page size so Host can be happy but I'll need to write less than a page.
I'll let you know when I finish this rework. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions. |
@dylad Is there anything new on this rework? |
Yeah I've polished it a bit and added multiple LUNs support. remove auto-init so users can select which MTD devices to export at runtime. |
Closing this one. |
19242: usbus/msc: add initial Mass Storage Class support r=benpicco a=dylad ### Contribution description This PR adds the initial support for Mass Storage Class in USBUS. This PR relies on the RIOT MTD implementation to implement the Mass Storage Class support. With the provided test application, a MTD device will be accessible as a normal storage device on your host computer. Read and Write operations are allowed. Multiple LUNs are supported so several MTD devices can be exported through USB. The MSC relies on SCSI protocol to operate. Currently there are some limitations: Supported host : Linux & Windows (macOS is untested) MSC cannot be used if MTD page size > 4096 MTD device must have at least 512 bytes of memory to be exported. Please be aware that performance are not so great. ### Testing procedure Flash `tests/usbus_msc` application on a board with at least one MTD device. Once the shell has started, prepare one or several MTD devices to be exported using `add_lun` command. Once ready, start the USB connection with `usb_attach` All MTD exported should appear as` /dev/sdX` on Linux. ### Issues/PRs references Supersede #15941 Co-authored-by: Dylan Laduranty <dylan.laduranty@mesotic.com>
Contribution description
This PR adds the initial support for Mass Storage Class in USBUS. This PR relies on the RIOT MTD implementation to implement the Mass Storage Class support. With the provided test application, a MTD device will be accessible as a normal storage device on your host computer
(Linux only for now).Read and Write operations are allowed.
The MSC relies on SCSI protocol to operate. SCSI is a beast and only a part of it has been implemented.
Currently there are some limitations:
Looks like some additional requests code must be supported to get it running on Windows-> Done.Regarding the page size, on the host side, Linux expects at least 512 bytes as page size. (512 is standard. 1024/2048 are accepted as some recent SSD make use of these values). This means that some logic was added in case the MTD page size is less than 512 bytes like SPI-NOR devices.
More logic must be added to handle page size superior to 512 bytes (e.g some flashpage implementation) but I think this should belongs to another PR.
Please be aware that performance are not so great. Be patient if you are using a big SD card.
And also, don't use a sd card if you have important data on it :)
Comments, especially on the MTD part, are very welcome !
Testing procedure
Flash
tests/usbus_msc
, on a board with a MTD device.Access your memory as a standard memory through /dev/sdX
You can use dd to read/write data or even format a FS on your memory device so you can access it the same way as a USB stick.
Issues/PRs references
None