📜 ⬆️ ⬇️

USB mass storage device and libopencm3

image




My work is related to the programming of microcontrollers, in particular STM32. For a long time, I used the STM32 Standard Peripheral Library to work with peripherals, as it is provided by the manufacturer and, accordingly, is the most complete. However, it is extremely inconvenient to work with it: initializing structures are often redundant, in the functions of the devil they break my leg, in general, very soon there is an overwhelming desire to get off this library and switch to something more accurate, correctly designed and written with “clean code”.
')
After a long search, the open source library libopencm3 was discovered, which met all the requirements. The reviews about her were positive and it turned out to be as pleasant as possible to work with her.

One of the last tasks at work was to raise USB MSD. To solve the problem, the STM32F4-discovery debug board and this example were used . The example did not start. There were two problems:
1. It was impossible to go to the disk and read the file located there.
2. Recognizing the device as a disk took more than 2 minutes.

All this was due to the presence of several bugs in the usb_msc.c file. Thus, in this article I will discuss how to correct these errors and continue to enjoy the library libopencm3 with pleasure.


Solution to problem number 1:
The essence of the error is that when the device receives a write request, it processes it correctly, but does not send back the processing status of the CSW request (Command Status Wrapper). Thus, the usb host (in our case, this is our PC) goes to the endless waiting for a response to the request, everything hangs, buggy, dies until you unplug the device =)

* Read more about Mass Storage Bulk-Only or CBI Transport Specification here .

Therefore, we find the function msc_data_rx_cb in the usb_msc.c file and bring it to the following form:
Added code snippet between slashes
static void msc_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) { usbd_mass_storage *ms; struct usb_msc_trans *trans; int len, max_len, left; void *p; ms = &_mass_storage; trans = &ms->trans; /* RX only */ left = sizeof(struct usb_msc_cbw) - trans->cbw_cnt; if (0 < left) { max_len = MIN(ms->ep_out_size, left); p = &trans->cbw.buf[0x1ff & trans->cbw_cnt]; len = usbd_ep_read_packet(usbd_dev, ep, p, max_len); trans->cbw_cnt += len; if (sizeof(struct usb_msc_cbw) == trans->cbw_cnt) { scsi_command(ms, trans, EVENT_CBW_VALID); if (trans->byte_count < trans->bytes_to_read) { /* We must wait until there is something to * read again. */ return; } } } if (trans->byte_count < trans->bytes_to_read) { if (0 < trans->block_count) { if ((0 == trans->byte_count) && (NULL != ms->lock)) { (*ms->lock)(); } } left = trans->bytes_to_read - trans->byte_count; max_len = MIN(ms->ep_out_size, left); p = &trans->msd_buf[0x1ff & trans->byte_count]; len = usbd_ep_read_packet(usbd_dev, ep, p, max_len); trans->byte_count += len; if (0 < trans->block_count) { if (0 == (0x1ff & trans->byte_count)) { uint32_t lba; lba = trans->lba_start + trans->current_block; if (0 != (*ms->write_block)(lba, trans->msd_buf)) { /* Error */ } trans->current_block++; } } /////////////////////ADD THIS////////////////////////////////////////////////////////////////////////////////// if (false == trans->csw_valid) { scsi_command(ms, trans, EVENT_NEED_STATUS); trans->csw_valid = true; } left = sizeof(struct usb_msc_csw) - trans->csw_sent; if (0 < left) { max_len = MIN(ms->ep_out_size, left); p = &trans->csw.buf[trans->csw_sent]; len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, max_len); trans->csw_sent += len; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } else if (trans->byte_count < trans->bytes_to_write) { if (0 < trans->block_count) { if ((0 == trans->byte_count) && (NULL != ms->lock)) { (*ms->lock)(); } if (0 == (0x1ff & trans->byte_count)) { uint32_t lba; lba = trans->lba_start + trans->current_block; if (0 != (*ms->read_block)(lba, trans->msd_buf)) { /* Error */ } trans->current_block++; } } left = trans->bytes_to_write - trans->byte_count; max_len = MIN(ms->ep_out_size, left); p = &trans->msd_buf[0x1ff & trans->byte_count]; len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, max_len); trans->byte_count += len; } else { if (0 < trans->block_count) { if (trans->current_block == trans->block_count) { uint32_t lba; lba = trans->lba_start + trans->current_block; if (0 != (*ms->write_block)(lba, trans->msd_buf)) { /* Error */ } trans->current_block = 0; if (NULL != ms->unlock) { (*ms->unlock)(); } } } if (false == trans->csw_valid) { scsi_command(ms, trans, EVENT_NEED_STATUS); trans->csw_valid = true; } left = sizeof(struct usb_msc_csw) - trans->csw_sent; if (0 < left) { max_len = MIN(ms->ep_out_size, left); p = &trans->csw.buf[trans->csw_sent]; len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, max_len); trans->csw_sent += len; } } } 



Hurray, now we can go to the disk to read the file!

Solution of the problem number 2:
The essence of this problem is that two SCSI commands are not implemented in the same usb_msc. file. This was revealed using a very useful program usblyser , which allows you to conveniently view the exchange of shipments between a usb device and a usb host.

So, firstly, the host does not receive a response to the READ_FORMAT_CAPACITIES command. Therefore, we add the scsi_read_format_capacities function to the usb_msc. file and we bring the scsi_command function to the following form:
View code
 static void scsi_read_format_capacities(usbd_mass_storage *ms, struct usb_msc_trans *trans, enum trans_event event) { if (EVENT_CBW_VALID == event) { trans->msd_buf[3] = 0x08; trans->msd_buf[4] = ms->block_count >> 24; trans->msd_buf[5] = 0xff & (ms->block_count >> 16); trans->msd_buf[6] = 0xff & (ms->block_count >> 8); trans->msd_buf[7] = 0xff & ms->block_count; trans->msd_buf[8] = 0x02; trans->msd_buf[9] = 0; trans->msd_buf[10] = 0x02; trans->msd_buf[11] = 0; trans->bytes_to_write = 9; set_sbc_status_good(ms); } } static void scsi_command(usbd_mass_storage *ms, struct usb_msc_trans *trans, enum trans_event event) { if (EVENT_CBW_VALID == event) { /* Setup the default success */ trans->csw_sent = 0; trans->csw.csw.dCSWSignature = CSW_SIGNATURE; trans->csw.csw.dCSWTag = trans->cbw.cbw.dCBWTag; trans->csw.csw.dCSWDataResidue = 0; trans->csw.csw.bCSWStatus = CSW_STATUS_SUCCESS; trans->bytes_to_write = 0; trans->bytes_to_read = 0; trans->byte_count = 0; } switch (trans->cbw.cbw.CBWCB[0]) { case SCSI_TEST_UNIT_READY: case SCSI_SEND_DIAGNOSTIC: /* Do nothing, just send the success. */ set_sbc_status_good(ms); break; case SCSI_FORMAT_UNIT: scsi_format_unit(ms, trans, event); break; case SCSI_REQUEST_SENSE: scsi_request_sense(ms, trans, event); break; case SCSI_MODE_SENSE_6: scsi_mode_sense_6(ms, trans, event); break; case SCSI_READ_6: scsi_read_6(ms, trans, event); break; case SCSI_INQUIRY: scsi_inquiry(ms, trans, event); break; case SCSI_READ_CAPACITY: scsi_read_capacity(ms, trans, event); break; case SCSI_READ_10: scsi_read_10(ms, trans, event); break; case SCSI_WRITE_6: scsi_write_6(ms, trans, event); break; case SCSI_WRITE_10: scsi_write_10(ms, trans, event); break; //////////////////ADD THIS/////////////////////////////////////////////////////////////////////////////////////////////////// case SCSI_READ_FORMAT_CAPACITIES: scsi_read_format_capacities(ms, trans, event); break; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// default: set_sbc_status(ms, SBC_SENSE_KEY_ILLEGAL_REQUEST, SBC_ASC_INVALID_COMMAND_OPERATION_CODE, SBC_ASCQ_NA); trans->bytes_to_write = 0; trans->bytes_to_read = 0; trans->csw.csw.bCSWStatus = CSW_STATUS_FAILED; break; } } 



Secondly, the host does not receive a response to the INQUIRY (SERIAL NUMBER) command. To correct this error, you need to create an _spc3_inquiry_sn_response array and bring the scsi_inquiry function to the following form:
View code
 static const uint8_t _spc3_inquiry_sn_response[20] = { 0x00, 0x80, 0x00, 0x10, //    .     16 (third pin 123456) 't','h','i','r','d',' ','p','i','n',' ','1','2','3','4','5','6' }; static void scsi_inquiry(usbd_mass_storage *ms, struct usb_msc_trans *trans, enum trans_event event) { if (EVENT_CBW_VALID == event) { uint8_t evpd; uint8_t *buf; buf = get_cbw_buf(trans); evpd = 1 & buf[1]; if (evpd == 0) { size_t len; trans->bytes_to_write = sizeof(_spc3_inquiry_response); memcpy(trans->msd_buf, _spc3_inquiry_response, sizeof(_spc3_inquiry_response)); len = strlen(ms->vendor_id); len = MIN(len, 8); memcpy(&trans->msd_buf[8], ms->vendor_id, len); len = strlen(ms->product_id); len = MIN(len, 16); memcpy(&trans->msd_buf[16], ms->product_id, len); len = strlen(ms->product_revision_level); len = MIN(len, 4); memcpy(&trans->msd_buf[32], ms->product_revision_level, len); trans->csw.csw.dCSWDataResidue = sizeof(_spc3_inquiry_response); set_sbc_status_good(ms); } /////////////////////////////////ADD THIS///////////////////////////////////////////////////////////////////////////// else if (evpd == 1) { trans->bytes_to_write = sizeof(_spc3_inquiry_sn_response); memcpy(trans->msd_buf, _spc3_inquiry_sn_response, sizeof(_spc3_inquiry_sn_response)); trans->csw.csw.dCSWDataResidue = sizeof(_spc3_inquiry_sn_response); set_sbc_status_good(ms); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// else { /* TODO: Add VPD 0x83 support */ /* TODO: Add VPD 0x00 support */ } } } 



* You can learn more about SCSI commands again here .

After all these surgical interventions, the library needs to be rebuilt, having previously executed the command make clean.

Soon I plan to make a pull request to the repository with libopencm3, but it remains to be assumed how soon the owners will make these changes to the library, and in the meantime we all need to be here and now.

I really hope that at least someone this article will relieve an extra headache and will be useful.

PS: Here lies our corporate fork of the library with the changes already made, if you lazily dig yourself.

Source: https://habr.com/ru/post/304924/


All Articles