1 /* Copyright (c) 2010-2011 mbed.org, MIT License
3 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
4 * and associated documentation files (the "Software"), to deal in the Software without
5 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
6 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
7 * Software is furnished to do so, subject to the following conditions:
9 * The above copyright notice and this permission notice shall be included in all copies or
10 * substantial portions of the Software.
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
13 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
15 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #define WRITE_PROTECT 0x04
27 #define CBW_Signature 0x43425355
28 #define CSW_Signature 0x53425355
31 #define TEST_UNIT_READY 0x00
32 #define REQUEST_SENSE 0x03
33 #define FORMAT_UNIT 0x04
35 #define MODE_SELECT6 0x15
36 #define MODE_SENSE6 0x1A
37 #define START_STOP_UNIT 0x1B
38 #define MEDIA_REMOVAL 0x1E
39 #define READ_FORMAT_CAPACITIES 0x23
40 #define READ_CAPACITY 0x25
46 #define MODE_SELECT10 0x55
47 #define MODE_SENSE10 0x5A
49 // MSC class specific requests
50 #define MSC_REQUEST_RESET 0xFF
51 #define MSC_REQUEST_GET_MAX_LUN 0xFE
53 #define DEFAULT_CONFIGURATION (1)
56 #define MAX_PACKET MAX_PACKET_SIZE_EPBULK
66 USBMSD::USBMSD(uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(vendor_id, product_id, product_release) {
68 memset((void *)&cbw, 0, sizeof(CBW));
69 memset((void *)&csw, 0, sizeof(CSW));
78 // Called in ISR context to process a class specific request
79 bool USBMSD::USBCallback_request(void) {
82 CONTROL_TRANSFER * transfer = getTransferPtr();
83 static uint8_t maxLUN[1] = {0};
85 if (transfer->setup.bmRequestType.Type == CLASS_TYPE) {
86 switch (transfer->setup.bRequest) {
87 case MSC_REQUEST_RESET:
91 case MSC_REQUEST_GET_MAX_LUN:
92 transfer->remaining = 1;
93 transfer->ptr = maxLUN;
94 transfer->direction = DEVICE_TO_HOST;
106 bool USBMSD::connect(bool blocking) {
107 //disk initialization
108 if (disk_status() & NO_INIT) {
109 if (disk_initialize()) {
114 // get number of blocks
115 BlockCount = disk_sectors();
118 MemorySize = disk_size();
120 if (BlockCount > 0) {
121 BlockSize = MemorySize / BlockCount;
122 if (BlockSize != 0) {
124 page = (uint8_t *)malloc(BlockSize * sizeof(uint8_t));
133 USBDevice::connect(blocking);
137 void USBMSD::disconnect() {
138 USBDevice::disconnect();
139 //De-allocate MSD page size:
144 void USBMSD::reset() {
149 // Called in ISR context called when a data is received
150 bool USBMSD::EPBULK_OUT_callback() {
152 uint8_t buf[MAX_PACKET_SIZE_EPBULK];
153 readEP(EPBULK_OUT, buf, &size, MAX_PACKET_SIZE_EPBULK);
155 // the device has to decode the CBW received
157 CBWDecode(buf, size);
160 // the device has to receive data from the host
165 memoryWrite(buf, size);
168 memoryVerify(buf, size);
173 // an error has occured: stall endpoint and send CSW
175 stallEndpoint(EPBULK_OUT);
176 csw.Status = CSW_ERROR;
181 //reactivate readings on the OUT bulk endpoint
182 readStart(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
186 // Called in ISR context when a data has been transferred
187 bool USBMSD::EPBULK_IN_callback() {
190 // the device has to send data to the host
200 //the device has to send a CSW
205 // the host has received the CSW -> we wait a CBW
210 // an error has occured
212 stallEndpoint(EPBULK_IN);
220 void USBMSD::memoryWrite (uint8_t * buf, uint16_t size) {
222 if ((addr + size) > MemorySize) {
223 size = MemorySize - addr;
225 stallEndpoint(EPBULK_OUT);
228 // we fill an array in RAM of 1 block before writing it in memory
229 for (int i = 0; i < size; i++)
230 page[addr%BlockSize + i] = buf[i];
232 // if the array is filled, write it in memory
233 if (!((addr + size)%BlockSize)) {
234 if (!(disk_status() & WRITE_PROTECT)) {
235 disk_write(page, addr/BlockSize, 1);
241 csw.DataResidue -= size;
243 if ((!length) || (stage != PROCESS_CBW)) {
244 csw.Status = (stage == ERROR) ? CSW_FAILED : CSW_PASSED;
249 void USBMSD::memoryVerify (uint8_t * buf, uint16_t size) {
252 if ((addr + size) > MemorySize) {
253 size = MemorySize - addr;
255 stallEndpoint(EPBULK_OUT);
258 // beginning of a new block -> load a whole block in RAM
259 if (!(addr%BlockSize))
260 disk_read(page, addr/BlockSize, 1);
262 // info are in RAM -> no need to re-read memory
263 for (n = 0; n < size; n++) {
264 if (page[addr%BlockSize + n] != buf[n]) {
272 csw.DataResidue -= size;
274 if ( !length || (stage != PROCESS_CBW)) {
275 csw.Status = (memOK && (stage == PROCESS_CBW)) ? CSW_PASSED : CSW_FAILED;
281 bool USBMSD::inquiryRequest (void) {
282 uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01,
283 36 - 4, 0x80, 0x00, 0x00,
284 'M', 'B', 'E', 'D', '.', 'O', 'R', 'G',
285 'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ',
288 if (!write(inquiry, sizeof(inquiry))) {
295 bool USBMSD::readFormatCapacity() {
296 uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08,
297 (uint8_t)((BlockCount >> 24) & 0xff),
298 (uint8_t)((BlockCount >> 16) & 0xff),
299 (uint8_t)((BlockCount >> 8) & 0xff),
300 (uint8_t)((BlockCount >> 0) & 0xff),
303 (uint8_t)((BlockSize >> 16) & 0xff),
304 (uint8_t)((BlockSize >> 8) & 0xff),
305 (uint8_t)((BlockSize >> 0) & 0xff),
307 if (!write(capacity, sizeof(capacity))) {
314 bool USBMSD::readCapacity (void) {
315 uint8_t capacity[] = {
316 (uint8_t)(((BlockCount - 1) >> 24) & 0xff),
317 (uint8_t)(((BlockCount - 1) >> 16) & 0xff),
318 (uint8_t)(((BlockCount - 1) >> 8) & 0xff),
319 (uint8_t)(((BlockCount - 1) >> 0) & 0xff),
321 (uint8_t)((BlockSize >> 24) & 0xff),
322 (uint8_t)((BlockSize >> 16) & 0xff),
323 (uint8_t)((BlockSize >> 8) & 0xff),
324 (uint8_t)((BlockSize >> 0) & 0xff),
326 if (!write(capacity, sizeof(capacity))) {
332 bool USBMSD::write (uint8_t * buf, uint16_t size) {
334 if (size >= cbw.DataLength) {
335 size = cbw.DataLength;
339 if (!writeNB(EPBULK_IN, buf, size, MAX_PACKET_SIZE_EPBULK)) {
343 csw.DataResidue -= size;
344 csw.Status = CSW_PASSED;
349 bool USBMSD::modeSense6 (void) {
350 uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 };
351 if (!write(sense6, sizeof(sense6))) {
357 void USBMSD::sendCSW() {
358 csw.Signature = CSW_Signature;
359 writeNB(EPBULK_IN, (uint8_t *)&csw, sizeof(CSW), MAX_PACKET_SIZE_EPBULK);
363 bool USBMSD::requestSense (void) {
364 uint8_t request_sense[] = {
367 0x05, // Sense Key: illegal request
385 if (!write(request_sense, sizeof(request_sense))) {
392 void USBMSD::fail() {
393 csw.Status = CSW_FAILED;
398 void USBMSD::CBWDecode(uint8_t * buf, uint16_t size) {
399 if (size == sizeof(cbw)) {
400 memcpy((uint8_t *)&cbw, buf, size);
401 if (cbw.Signature == CBW_Signature) {
403 csw.DataResidue = cbw.DataLength;
404 if ((cbw.CBLength < 1) || (cbw.CBLength > 16) ) {
408 case TEST_UNIT_READY:
420 case READ_FORMAT_CAPACITIES:
421 readFormatCapacity();
428 if (infoTransfer()) {
429 if ((cbw.Flags & 0x80)) {
433 stallEndpoint(EPBULK_OUT);
434 csw.Status = CSW_ERROR;
441 if (infoTransfer()) {
442 if (!(cbw.Flags & 0x80)) {
445 stallEndpoint(EPBULK_IN);
446 csw.Status = CSW_ERROR;
452 if (!(cbw.CB[1] & 0x02)) {
453 csw.Status = CSW_PASSED;
457 if (infoTransfer()) {
458 if (!(cbw.Flags & 0x80)) {
462 stallEndpoint(EPBULK_IN);
463 csw.Status = CSW_ERROR;
469 csw.Status = CSW_PASSED;
481 void USBMSD::testUnitReady (void) {
483 if (cbw.DataLength != 0) {
484 if ((cbw.Flags & 0x80) != 0) {
485 stallEndpoint(EPBULK_IN);
487 stallEndpoint(EPBULK_OUT);
491 csw.Status = CSW_PASSED;
496 void USBMSD::memoryRead (void) {
499 n = (length > MAX_PACKET) ? MAX_PACKET : length;
501 if ((addr + n) > MemorySize) {
502 n = MemorySize - addr;
506 // we read an entire block
507 if (!(addr%BlockSize))
508 disk_read(page, addr/BlockSize, 1);
510 // write data which are in RAM
511 writeNB(EPBULK_IN, &page[addr%BlockSize], n, MAX_PACKET_SIZE_EPBULK);
516 csw.DataResidue -= n;
518 if ( !length || (stage != PROCESS_CBW)) {
519 csw.Status = (stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED;
520 stage = (stage == PROCESS_CBW) ? SEND_CSW : stage;
525 bool USBMSD::infoTransfer (void) {
528 // Logical Block Address of First Block
529 n = (cbw.CB[2] << 24) | (cbw.CB[3] << 16) | (cbw.CB[4] << 8) | (cbw.CB[5] << 0);
531 addr = n * BlockSize;
533 // Number of Blocks to transfer
538 n = (cbw.CB[7] << 8) | (cbw.CB[8] << 0);
543 n = (cbw.CB[6] << 24) | (cbw.CB[7] << 16) | (cbw.CB[8] << 8) | (cbw.CB[9] << 0);
547 length = n * BlockSize;
549 if (!cbw.DataLength) { // host requests no data
550 csw.Status = CSW_FAILED;
555 if (cbw.DataLength != length) {
556 if ((cbw.Flags & 0x80) != 0) {
557 stallEndpoint(EPBULK_IN);
559 stallEndpoint(EPBULK_OUT);
562 csw.Status = CSW_FAILED;
574 // Called in ISR context
575 // Set configuration. Return false if the
576 // configuration is not supported.
577 bool USBMSD::USBCallback_setConfiguration(uint8_t configuration) {
578 if (configuration != DEFAULT_CONFIGURATION) {
582 // Configure endpoints > 0
583 addEndpoint(EPBULK_IN, MAX_PACKET_SIZE_EPBULK);
584 addEndpoint(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
587 readStart(EPBULK_OUT, MAX_PACKET_SIZE_EPBULK);
592 uint8_t * USBMSD::stringIinterfaceDesc() {
593 static uint8_t stringIinterfaceDescriptor[] = {
595 STRING_DESCRIPTOR, //bDescriptorType 0x03
596 'M',0,'S',0,'D',0 //bString iInterface - MSD
598 return stringIinterfaceDescriptor;
601 uint8_t * USBMSD::stringIproductDesc() {
602 static uint8_t stringIproductDescriptor[] = {
604 STRING_DESCRIPTOR, //bDescriptorType 0x03
605 'M',0,'b',0,'e',0,'d',0,' ',0,'M',0,'S',0,'D',0 //bString iProduct - Mbed Audio
607 return stringIproductDescriptor;
611 uint8_t * USBMSD::configurationDesc() {
612 static uint8_t configDescriptor[] = {
616 2, // bDescriptorType
617 LSB(9 + 9 + 7 + 7), // wTotalLength
619 0x01, // bNumInterfaces
620 0x01, // bConfigurationValue: 0x01 is used to select this configuration
621 0x00, // iConfiguration: no string to describe this configuration
622 0xC0, // bmAttributes
623 100, // bMaxPower, device power consumption is 100 mA
625 // Interface 0, Alternate Setting 0, MSC Class
627 4, // bDescriptorType
628 0x00, // bInterfaceNumber
629 0x00, // bAlternateSetting
630 0x02, // bNumEndpoints
631 0x08, // bInterfaceClass
632 0x06, // bInterfaceSubClass
633 0x50, // bInterfaceProtocol
636 // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
638 5, // bDescriptorType
639 PHY_TO_DESC(EPBULK_IN), // bEndpointAddress
640 0x02, // bmAttributes (0x02=bulk)
641 LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
642 MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
645 // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
647 5, // bDescriptorType
648 PHY_TO_DESC(EPBULK_OUT), // bEndpointAddress
649 0x02, // bmAttributes (0x02=bulk)
650 LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
651 MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
654 return configDescriptor;