Попалась задача в проекте реализовать, чтобы по USB микроконтроллер прикидывался несколькими дисковыми устройствами для MicroSD, встроенной EEPROM и нескольких страничек оперативной памяти. Решил, что вполне логично, пойти по пути наименьшего сопротивления, попробовав запустить из коробки, то что ST реализовали в своей библиотеке. Работа c USB разделена у них на уровни абстракции: драйвер + MiddleWare:
Ошибки у нас закрались MiddleWare и благодаря тому, что MiddleWare максимально абстрагирован от железа, этом метод позволит исправить реализацию и для остальных семейств с минимальными правками кода. Подробное описание MiddleWare расписано у ST в UM1717, UM0424, UM1021, UM1720.
Запускаем из коробки (в чистом виде, что ST предоставил)
Создаём в кубе проект, застраиваем USB и выбираем в MiddleWare Class For FS IP: Mass Storage Class и правим при желании дескрипторы. Затем в файле USB_DEVICE->App->usbd_storage_if.c прописываем логику работы нашего устройства и описание LUN. Привожу изменённую часть файла:
/* USER CODE BEGIN PRIVATE_TYPES */
typedef enum LUN_NUM{
LUN_NUM_MICROSD = 0,
LUN_NUM_EEPROM,
LUN_NUM_SYSTEM,
LUN_NUM_TEST
} lun_num_t;
/* USER CODE END PRIVATE_TYPES */
/* USER CODE BEGIN PRIVATE_DEFINES */
#define STORAGE_LUNS 0x8
#define EEPROM_BLOCK_SIZE 512
#define EEPROM_BLOCKS 64
/* USER CODE END PRIVATE_DEFINES */
/** USB Mass storage Standard Inquiry Data. */
const int8_t STORAGE_Inquirydata_FS[] = {/* 36 */
/* LUN 0 */
0x00,
0x00, // Disable MSB bit - for multipartishion recognize in windows, removeble device = 0x80
0x02,
0x02,
(STANDARD_INQUIRY_DATA_LEN - 5),
0x00,
0x00,
0x00,
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
'M', 'i', 'c', 'r', 'o', 'S', 'D', ' ', /* Product : 16 Bytes */
'D', 'e', 'v', 'i', 'c', 'e', ' ', ' ',
'0', '.', '0' ,'1', /* Version : 4 Bytes */
/* LUN 1 */
0x00, // bit4:0 - peripheral device type, bit7:5 - reserved
0x00, // bit6:0 - reserved, bit7 - removable media bit (set to 1 to indicate removable media)
0x02, // bit2:0 - ANSI version, bit5:3 - ECMA version, bit7:6 - ISO version
0x02, // bit3:0 - Response data format, bit7:4 - reserved
(STANDARD_INQUIRY_DATA_LEN - 5), // additional length - specify the length in bytes of the parameters.
0x00, // reserved
0x00, // reserved
0x00, // reserved
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer: 8 bytes */
'E', 'E', 'P', 'R', 'O', 'M', ' ', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0', '1', /* Version : 4 Bytes */
/* LUN 2 */
0x00, // bit4:0 - peripheral device type, bit7:5 - reserved
0x00, // bit6:0 - reserved, bit7 - removable media bit (set to 1 to indicate removable media)
0x02, // bit2:0 - ANSI version, bit5:3 - ECMA version, bit7:6 - ISO version
0x02, // bit3:0 - Response data format, bit7:4 - reserved
(STANDARD_INQUIRY_DATA_LEN - 5), // additional length - specify the length in bytes of the parameters.
0x00, // reserved
0x00, // reserved
0x00, // reserved
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer: 8 bytes */
'S', 'y', 's', 't', 'e', 'm', ' ', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0', '1', /* Version : 4 Bytes */
/* LUN 3 */
0x00, // bit4:0 - peripheral device type, bit7:5 - reserved
0x00, // bit6:0 - reserved, bit7 - removable media bit (set to 1 to indicate removable media)
0x02, // bit2:0 - ANSI version, bit5:3 - ECMA version, bit7:6 - ISO version
0x02, // bit3:0 - Response data format, bit7:4 - reserved
(STANDARD_INQUIRY_DATA_LEN - 5), // additional length - specify the length in bytes of the parameters.
0x00, // reserved
0x00, // reserved
0x00, // reserved
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer: 8 bytes */
'T', 'e', 's', 't', ' ', ' ', ' ', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0', '1', /* Version : 4 Bytes */
/* LUN 4 */
0x00,
0x00, // Disable MSB bit - for multipartishion recognize in windows, removeble device = 0x80
0x02,
0x02,
(STANDARD_INQUIRY_DATA_LEN - 5),
0x00,
0x00,
0x00,
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
'M', 'i', 'c', 'r', 'o', 'S', 'D', '2', /* Product : 16 Bytes */
'D', 'e', 'v', 'i', 'c', 'e', ' ', ' ',
'0', '.', '0' ,'1', /* Version : 4 Bytes */
/* LUN 5 */
0x00, // bit4:0 - peripheral device type, bit7:5 - reserved
0x00, // bit6:0 - reserved, bit7 - removable media bit (set to 1 to indicate removable media)
0x02, // bit2:0 - ANSI version, bit5:3 - ECMA version, bit7:6 - ISO version
0x02, // bit3:0 - Response data format, bit7:4 - reserved
(STANDARD_INQUIRY_DATA_LEN - 5), // additional length - specify the length in bytes of the parameters.
0x00, // reserved
0x00, // reserved
0x00, // reserved
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer: 8 bytes */
'E', 'E', 'P', 'R', 'O', 'M', '2', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0', '1', /* Version : 4 Bytes */
/* LUN 6 */
0x00, // bit4:0 - peripheral device type, bit7:5 - reserved
0x00, // bit6:0 - reseForrved, bit7 - removable media bit (set to 1 to indicate removable media)
0x02, // bit2:0 - ANSI version, bit5:3 - ECMA version, bit7:6 - ISO version
0x02, // bit3:0 - Response data format, bit7:4 - reserved
(STANDARD_INQUIRY_DATA_LEN - 5), // additional length - specify the length in bytes of the parameters.
0x00, // reserved
0x00, // reserved
0x00, // reserved
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer: 8 bytes */
'S', 'y', 's', 't', 'e', 'm', '2', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0', '1', /* Version : 4 Bytes */
/* LUN 7 */
0x00, // bit4:0 - peripheral device type, bit7:5 - reserved
0x00, // bit6:0 - reserved, bit7 - removable media bit (set to 1 to indicate removable media)
0x02, // bit2:0 - ANSI version, bit5:3 - ECMA version, bit7:6 - ISO version
0x02, // bit3:0 - Response data format, bit7:4 - reserved
(STANDARD_INQUIRY_DATA_LEN - 5), // additional length - specify the length in bytes of the parameters.
0x00, // reserved
0x00, // reserved
0x00, // reserved
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer: 8 bytes */
'T', 'e', 's', 't', '2', ' ', ' ', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0', '1', /* Version : 4 Bytes */
};
/* USER CODE END INQUIRY_DATA_FS */
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
/* USER CODE BEGIN 3 */
switch(lun){
case LUN_NUM_MICROSD:
*block_num = sd_card_glb->data.size_kb << 1;
*block_size = sd_card_glb->data.block_size;
break;
case LUN_NUM_EEPROM:
*block_num = EEPROM_BLOCKS;
*block_size = EEPROM_BLOCK_SIZE;
break;
case LUN_NUM_SYSTEM:
*block_num = EEPROM_BLOCKS << 2;
*block_size = EEPROM_BLOCK_SIZE;
break;
case LUN_NUM_TEST:
*block_num = EEPROM_BLOCKS << 4;
*block_size = EEPROM_BLOCK_SIZE;
break;
case 0x04:
*block_num = EEPROM_BLOCKS << 5;
*block_size = EEPROM_BLOCK_SIZE;
break;
case 0x05:
*block_num = EEPROM_BLOCKS << 6;
*block_size = EEPROM_BLOCK_SIZE;
break;
case 0x06:
*block_num = EEPROM_BLOCKS << 7;
*block_size = EEPROM_BLOCK_SIZE;
break;
case 0x07:
*block_num = EEPROM_BLOCKS << 8;
*block_size = EEPROM_BLOCK_SIZE;
break;
}
return (USBD_OK);
/* USER CODE END 3 */
}
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
switch(lun){
case LUN_NUM_MICROSD:
sd_card_read_single_block(sd_card_glb, blk_addr, buf);
/* for (int i =0 ; i<512; i++){
buf[i] = blk_addr;
}*/
break;
case LUN_NUM_EEPROM:
for (int i =0 ; i<512; i++){
buf[i] = i;
}
break;
case LUN_NUM_SYSTEM:
for (int i =0 ; i<512; i++){
buf[i] = blk_addr;
}
break;
case LUN_NUM_TEST:
for (int i =0 ; i<512; i++){
buf[i] = lun;
}
break;
}
return (USBD_OK);
/* USER CODE END 6 */
}
Собираем проект, запускаем и видим, что у нас вместо тестовых 8 LUN определилось только 2, да и нормально работают, если в STORAGE_GetCapacity_FS выдать одинаковый размер для обоих LUN, да ещё устройство переодически сбрасывается...
В kernel log:
usb 1-1: new full-speed USB device number 10 using xhci_hcd
usb 1-1: New USB device found, idVendor=0483, idProduct=572a, bcdDevice= 2.00
usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-1: Product: Test MASS Storage
usb 1-1: Manufacturer: Developer
usb 1-1: SerialNumber: 2062369C5950
usb-storage 1-1:1.0: USB Mass Storage device detected
scsi host4: usb-storage 1-1:1.0
scsi 4:0:0:0: Direct-Access STM MicroSD Device 0.01 PQ: 0 ANSI: 2
scsi 4:0:0:1: Direct-Access STM EEPROM 0.01 PQ: 0 ANSI: 2
scsi 4:0:0:0: Attached scsi generic sg1 type 0
scsi 4:0:0:1: Attached scsi generic sg2 type 0
sd 4:0:0:0: [sdb] 1030144 512-byte logical blocks: (527 MB/503 MiB)
sd 4:0:0:1: [sdc] 64 512-byte logical blocks: (32.8 kB/32.0 KiB)
sd 4:0:0:1: [sdc] Write Protect is off
sd 4:0:0:1: [sdc] Mode Sense: 22 0 0 0
sd 4:0:0:1: [sdc] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
sdb: sdb1 sdb2 sdb3
usb 1-1: reset full-speed USB device number 10 using xhci_hcd
sd 4:0:0:0: [sdb] Attached SCSI disk
usb 1-1: reset full-speed USB device number 10 using xhci_hcd
usb 1-1: reset full-speed USB device number 10 using xhci_hcd
sd 4:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE cmd_age=0s
sd 4:0:0:0: [sdb] tag#0 Sense Key : Illegal Request [current]
sd 4:0:0:0: [sdb] tag#0 Add. Sense: Logical block address out of range
sd 4:0:0:0: [sdb] tag#0 CDB: Read(10) 28 0 0 0f b7 f8 0 0 1 0
blk_update_request: critical target error, dev sdb, sector 1030136 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 0
sd 4:0:0:1: [sdc] Attached SCSI disk
sd 4:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE cmd_age=0s
sd 4:0:0:0: [sdb] tag#0 Sense Key : Illegal Request [current]
sd 4:0:0:0: [sdb] tag#0 Add. Sense: Logical block address out of range
sd 4:0:0:0: [sdb] tag#0 CDB: Read(10) 28 0 0 0f b7 f8 0 0 1 0
blk_update_request: critical target error, dev sdb, sector 1030136 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
buffer_io_error: 1 callbacks suppressed
Buffer I/O error on dev sdb, logical block 128767 async page read
В логе USB, захваченным Wireshark:
запрос USBMS 64 GET MAX LUN - отдаёт 7 (LUN нумеруются с 0 и отдаётся максимально возможный номер), а LUN 2 и остальные - игнорируются
ответы на запросы SCSI: Data In LUN: (Mode Sense(6) - говорят что получены неправильные данные от устройства, а это данные о параметров каждого LUN, в частности, делающие их readonly
не знает команду USBMS 95 SCSI Command: 0xa1 LUN:0x00 - команда ATA Command Pass-Through, после которой идёт переинициализация устройства по шине c продолжением работы:
386 4.538519 1.10.1 host USBMS 77 SCSI: Response LUN: 0x00 (Read Capacity(10)) (Good)
387 4.538572 host 1.10.1 USBMS 95 SCSI Command: 0xa1 LUN:0x00
388 4.538641 1.10.1 host USB 64 URB_BULK out
389 4.538657 host 1.10.1 USB 64 URB_BULK in
390 4.538711 1.10.1 host USB 64 URB_BULK in
391 4.538726 host 1.10.0 USB 64 CLEAR FEATURE Request
392 4.538810 1.10.0 host USB 64 CLEAR FEATURE Response
393 4.538824 host 1.10.1 USB 64 URB_BULK in
394 4.538925 1.10.1 host USB 64 URB_BULK in
395 4.538937 host 1.10.0 USB 64 CLEAR FEATURE Request
396 4.539014 1.10.0 host USB 64 CLEAR FEATURE Response
397 4.539067 host 1.10.1 USB 64 URB_BULK in
398 4.539125 1.10.1 host USB 64 URB_BULK in
399 4.539169 host 1.10.0 USB 64 CLEAR FEATURE Request
400 4.539254 1.10.0 host USB 64 CLEAR FEATURE Response
415 4.815078 host 1.10.0 USB 64 GET DESCRIPTOR Request DEVICE
416 4.815289 1.10.0 host USB 82 GET DESCRIPTOR Response DEVICE
417 4.815371 host 1.10.0 USB 64 GET DESCRIPTOR Request BOS
418 4.815507 1.10.0 host USB 69 GET DESCRIPTOR Response BOS
419 4.815584 host 1.10.0 USB 64 GET DESCRIPTOR Request BOS
420 4.815802 1.10.0 host USB 76 GET DESCRIPTOR Response BOS
421 4.815898 host 1.10.0 USB 64 GET DESCRIPTOR Request CONFIGURATION
422 4.816161 1.10.0 host USB 96 GET DESCRIPTOR Response CONFIGURATION
423 4.816257 host 1.10.0 USB 64 GET DESCRIPTOR Request CONFIGURATION
424 4.816527 1.10.0 host USB 96 GET DESCRIPTOR Response CONFIGURATION
425 4.816616 host 1.10.0 USB 64 GET DESCRIPTOR Request CONFIGURATION
426 4.816874 1.10.0 host USB 96 GET DESCRIPTOR Response CONFIGURATION
427 4.816959 host 1.10.0 USB 64 GET DESCRIPTOR Request CONFIGURATION
428 4.817224 1.10.0 host USB 96 GET DESCRIPTOR Response CONFIGURATION
429 4.817309 host 1.10.0 USB 64 GET DESCRIPTOR Request STRING
430 4.817470 1.10.0 host USB 90 GET DESCRIPTOR Response STRING
431 4.818177 host 1.10.0 USB 64 SET CONFIGURATION Request
432 4.818338 1.10.0 host USB 64 SET CONFIGURATION Response
433 4.818507 host 1.10.1 USBMS 95 SCSI: Read(10) LUN: 0x03 (LBA: 0x00000008, Len: 8)
В итоге, библиотека не работает с несколькими LUN, разве что при одном условии у нас только 2 LUN и их размеры равны. Обнаруженные проблемы:
-
Независимо от указанного нами количество LUN в ответе на call back функцию int8_t STORAGE_GetMaxLun_FS(void) - отдаёт правильное количесво LUN, но LUN больше 1 - игнорируются
-
Если LUNы разного размера - возможно корректно работать только с меньшим из них, иначе, судя по логам - недопустимый для чтения номер блока
-
Постоянный сброс устройства по шине из-за отсутствия ответа на команду SCSI 0xa1 ATA Command Pass-Through
Для лучшего понимания, что мы будем дальше делать, кратко расмотрим работу Mass Storage с интерфейсом BOT
Описание работы USB Mass Storage BOT
Протокол BOT расшифровывается как Bulk Only Transfer, который ещё также называют BBB, так как все 3 фазы обмена (команд/данных/статуса) используют конечные точки. Существует ещё протокол CBI - Control Buck Interrupt, использующийся, в основном, для full-speed floppy дисководов.
На рисунке ниже изображена передача команд, данных и статуса по протоколу BOT. Контейнер командного блока CBW представляет из себя короткий пакет, длиной 31 байт. CBW и все последующие данные с контейнером статуса команды CSW представлют из себя пакет. Ещё одна особенность, это кодировка CBW - little-endian с LSB (0 байт идёт первым).
В интерфейс BOT завернут транспортный протокол SCSI (Small Computer System Interface), разработанный ещё в 1978 году настолько удачным, что используется до сих пор для взаимодействия с блочными носителями данных. Работа по SCSI выглядит следующим образом:
-
Устройству хост отправляет команду с параметрами: SCSI: [команда + параметры]
-
Устройство отдаёт хосту данные: SCSI Payload (Inquiry Response Data)
-
Устройство отдаёт хосту статус: SCSI: Response (Inquiry) [статус]
Рассмотрим минимальных набор команд для определения нашего устройство системой. Команды взаимодействия с нашим устройством по USB выглядят следующим образом:
Пртокол |
Направление |
Команда |
Данные |
USB |
-> |
Запрос дескрипторов устройства |
Тип дескрипторов |
USB |
<- |
DESCRIPTOR Request |
Дескрипторы |
USBMS |
-> |
Запрос максимального номера LUN |
|
USBMS |
<- |
GET MAX LUN Response |
Максимальный LUN |
SCSI |
-> |
Запрос структуры LUN |
номер LUN |
SCSI |
<- |
SCSI: Data In LUN: 0xyy (Inquiry Response Data) |
Структура LUN |
SCSI |
-> |
Юнит тест LUN |
Номер LUN |
SCSI |
<- |
SCSI: Response LUN: 0xyy (Test Unit Ready) |
статус LUN |
SCSI |
-> |
Запрос ёмкости LUN: размер и количество блоков |
номер LUN |
SCSI |
<- |
SCSI: Data In LUN: 0x01 (Read Capacity(10) Response Data) |
размер и количество блоков LUN |
SCSI |
-> |
Запрос режимов |
номер LUN |
SCSI |
<- |
SCSI: Data In LUN: 0x01 (Mode Sense(6) Response Data) |
Режимы LUN, в том числе read-only |
SCSI |
-> |
Чтение блока |
номер LUN, номер блоков, количество блоков |
SCSI |
<- |
SCSI: Read(10) LUN: 0x03 (LBA: 0x00000000, Len: 8) |
Прочитанные данные |
Таким образом, после запроса дискрипторов, запроса готовности LUN и запроса ёмкости, драйвером операционной системы производится чтение блоков: вычитывается таблица разделов (MBR или GPT), затем сами разделы для определения их параметров и файловой системы, а затем последние блоки из заявленной ёмкости.
Перейдём теперь к структуре библиотек ST:
Файл |
Назначение |
|
USB_DEVICE->App->usb_device.c |
Функции инициализации USB, здесь можно описать композитные устройства или переключение устройств |
|
USB_DEVICE->App->usbd_desk.c |
Дескриптор устройства DEVICE DESCRIPTOR (VID/PID), информация о производителе. Структура USBD_FS_DeviceDescUSBD_FS_DeviceDesc |
|
Middlewares->ST->STM32_USB_Device_Library->Class->MSC->Src->usbd_msc.c |
Дескрипторы конфигурации, интерфейса и конечных точек MassStorage и функции инициализации USBD_MSC_CfgFSDesc |
|
Middlewares->ST->STM32_USB_Device_Library->Class->MSC->Src->usbd_msc_bot.c |
Функции интерфейса BOT |
|
Middlewares->ST->STM32_USB_Device_Library->Class->MSC->Src->usbd_msc_data.c |
Структуры ответов на некоторые команды SCSI, в частности на MODE_SENSE6 и MSC_Mode_Sense10 |
|
Middlewares->ST->STM32_USB_Device_Library->Class->MSC->Src->usbd_msc_scsi.c |
Функции отправки и обработки SCSI команд интерфейса |
|
USB_DEVICE->App->usbd_storage_if.с |
Здесь мы описываем структуру LUN и функции взаимодействия с ними и с источниками данных |
После того, как разобрались как всё работает и с чем будем иметь дело приступаем к починке.
1. Включаем больше 2 LUN
Исходя из специфики интерфейса BOT, максимальное количество поддерживаемых LUN равно 15 или 0x0f, видимо, больше бит жалко было, да и задач себе не представляю для микроконтроллера с 15-ю дисками, хотя... если очень хочется, можно сделать устройство композитным и навесить ещё 1 интерфейс со своими функциями обработки.
Драйверами операционных систем что Linux, что Windows, увидеть больше 8 LUN не получалось. Судя по анадизу трафика USB запрос GET MAX LUN больше 7 съедает, в логи драйвер не ругается, но 8-ое и более старшее устройство опросить даже не пытается...
Но библиотеки ST не идеальны и видимо не проходят должного тестирования функционала. Такое ощущение, что сидят студенты, проверят базовые функции и на этом заканчивают, убедившись, что раз работает - значит сойдет. Так и в нашем случае, протестировали с 1 LUN и успокоились на этом, при этом с 2-мя работает очень криво при равных размерах, а большем - вообще никак. Ну что же делать, остаётся нам править имеющееся, чтобы можно было пользоваться.
Тк у нас отсутствует отбработка SCSI команд для LUN, начиная с 2 и более, ищем функцию
static void MSC_BOT_CBW_Decode(USBD_HandleTypeDef *pdev)
формирования SCSI команд Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_bot.c и видим ограничение номера LUN константой 1U! Правим, на максимальное, заданное нами количество LUN hmsc->max_lun:
@@ -255,7 +255,7 @@ static void MSC_BOT_CBW_Decode(USBD_HandleTypeDef *pdev)
if ((USBD_LL_GetRxDataSize(pdev, MSC_EPOUT_ADDR) != USBD_BOT_CBW_LENGTH) ||
(hmsc->cbw.dSignature != USBD_BOT_CBW_SIGNATURE) ||
- (hmsc->cbw.bLUN > 1U) || (hmsc->cbw.bCBLength < 1U) ||
+ (hmsc->cbw.bLUN > hmsc->max_lun) || (hmsc->cbw.bCBLength < 1U) ||
(hmsc->cbw.bCBLength > 16U))
{
SCSI_SenseCode(pdev, hmsc->cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB);
Но интерфейсом у нас ограничено максимальное количество 0x0f, поэтому ограничем максимально задаваемое количество LUN этим числом, добавив константу в файл Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Inc/usbd_msc.h:
@@ -55,6 +55,7 @@ extern "C" {
#define BOT_RESET 0xFF
#define USB_MSC_CONFIG_DESC_SIZ 32
+#define MSC_BOT_MAX_LUN 0x0F
#define MSC_EPIN_ADDR 0x81U
#define MSC_EPOUT_ADDR 0x01U
@@ -82,25 +83,31 @@ typedef struct _USBD_STORAGE
Ограничивать будем hmsc->max_lun, получаемое из CALLBACK
int8_t STORAGE_GetMaxLun_FS(void)
{
/* USER CODE BEGIN 8 */
return (STORAGE_LUNS - 1);
/* USER CODE END 8 */
}
файла USB_DEVICE->App->usbd_storage_if.c
Ограничиваем результат вызова CALLBACK в файле Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc.c
@@ -390,6 +390,7 @@ uint8_t USBD_MSC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
((req->bmRequest & 0x80U) == 0x80U))
{
hmsc->max_lun = (uint32_t)((USBD_StorageTypeDef *)pdev->pUserData)->GetMaxLun();
+ hmsc->max_lun = (hmsc->max_lun > MSC_BOT_MAX_LUN) ? MSC_BOT_MAX_LUN : hmsc->max_lun;
(void)USBD_CtlSendData(pdev, (uint8_t *)&hmsc->max_lun, 1U);
}
else
После этого на хост стали приходить данне от всех 8 LUN, но радовться ещё рано, так как если размер LUN разный - блок больший минимального не вычитывается при перекрёстном опросе LUN.
2. Чиним использование нескольких LUN c разными размерами блоков и их количеством
Ошибка о вылете из адресного пространства номера блока исходит из функции проверки диапазона адресов
static int8_t SCSI_CheckAddressRange(USBD_HandleTypeDef *pdev, uint8_t lun,
uint32_t blk_offset, uint32_t blk_nbr)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
if (hmsc == NULL)
{
return -1;
}
if ((blk_offset + blk_nbr) > hmsc->scsi_blk_nbr)
{
SCSI_SenseCode(pdev, lun, ILLEGAL_REQUEST, ADDRESS_OUT_OF_RANGE);
return -1;
}
return 0;
}
файла Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c. Внимательно присмотревшись, видим, что номер текущего блока сравнивается со считанным до этого количеством блоков hmsc->scsi_blk_nbr. Учитывая специфику протокола SCSI, хост не обязан перед каждой операцией запрашивать размер и количество блоков читаемого/записываемого LUN и, соответственно, там могут остаться параметры другого LUN, что мы и видим. Вот как считывается количество и размер блоков LUN на примере SCSI_ReadCapacity16, для SCSI_ReadCapacity10 аналогично:
static int8_t SCSI_ReadCapacity16(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
UNUSED(params);
uint8_t idx;
int8_t ret;
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
if (hmsc == NULL)
{
return -1;
}
ret = ((USBD_StorageTypeDef *)pdev->pUserData)->GetCapacity(lun, &hmsc->scsi_blk_nbr, &hmsc->scsi_blk_size);
if ((ret != 0) || (hmsc->scsi_medium_state == SCSI_MEDIUM_EJECTED))
{
SCSI_SenseCode(pdev, lun, NOT_READY, MEDIUM_NOT_PRESENT);
return -1;
}
hmsc->bot_data_length = ((uint32_t)params[10] << 24) |
((uint32_t)params[11] << 16) |
((uint32_t)params[12] << 8) |
(uint32_t)params[13];
for (idx = 0U; idx < hmsc->bot_data_length; idx++)
{
hmsc->bot_data[idx] = 0U;
}
hmsc->bot_data[4] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 24);
hmsc->bot_data[5] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 16);
hmsc->bot_data[6] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 8);
hmsc->bot_data[7] = (uint8_t)(hmsc->scsi_blk_nbr - 1U);
hmsc->bot_data[8] = (uint8_t)(hmsc->scsi_blk_size >> 24);
hmsc->bot_data[9] = (uint8_t)(hmsc->scsi_blk_size >> 16);
hmsc->bot_data[10] = (uint8_t)(hmsc->scsi_blk_size >> 8);
hmsc->bot_data[11] = (uint8_t)(hmsc->scsi_blk_size);
hmsc->bot_data_length = ((uint32_t)params[10] << 24) |
((uint32_t)params[11] << 16) |
((uint32_t)params[12] << 8) |
(uint32_t)params[13];
return 0;
}
Те видим, что проверка адресов будет проходить только по последнему результату! Как не трудно догадаться, для исправления нам необходимо, чтобы для каждого LUN все параметры сохранялись индивидуально, те массив. PS: Оптимальнее всего было-бы вообще выкинуть CALLBACK STORAGE_GetMaxLun_FS и сразу заполнить структуру параметрами всех LUN, но учитывая, неоптимальность всего остального, это капля в море, тк оптимизировать там надо многое...
Для быстрого испавления в файле Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Inc/usbd_msc.h создадим вспомогательную струтуру USBD_LUN_BLK_HandleTypeDef и исправим существующую USBD_LUN_BLK_HandleTypeDef:
typedef struct
{
uint32_t max_lun;
uint32_t interface;
uint8_t bot_state;
uint8_t bot_status;
uint32_t bot_data_length;
uint8_t bot_data[MSC_MEDIA_PACKET];
USBD_MSC_BOT_CBWTypeDef cbw;
USBD_MSC_BOT_CSWTypeDef csw;
USBD_SCSI_SenseTypeDef scsi_sense [SENSE_LIST_DEEPTH];
uint8_t scsi_sense_head;
uint8_t scsi_sense_tail;
uint8_t scsi_medium_state;
uint16_t scsi_blk_size;
uint32_t scsi_blk_nbr;
uint32_t scsi_blk_addr;
uint32_t scsi_blk_len;
}
USBD_MSC_BOT_HandleTypeDef;
Следующим образом:
typedef struct
{
uint16_t size;
uint32_t nbr;
uint32_t addr;
uint32_t len;
}
USBD_LUN_BLK_HandleTypeDef;
typedef struct
{
uint32_t max_lun;
uint32_t interface;
uint8_t bot_state;
uint8_t bot_status;
uint32_t bot_data_length;
uint8_t bot_data[MSC_MEDIA_PACKET];
USBD_MSC_BOT_CBWTypeDef cbw;
USBD_MSC_BOT_CSWTypeDef csw;
USBD_SCSI_SenseTypeDef scsi_sense [SENSE_LIST_DEEPTH];
uint8_t scsi_sense_head;
uint8_t scsi_sense_tail;
uint8_t scsi_medium_state;
USBD_LUN_BLK_HandleTypeDef scsi_blk[MSC_BOT_MAX_LUN];
}
USBD_MSC_BOT_HandleTypeDef;
Затем, нам необходимо исправить обраработчики команд SCSI в файле Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c:
@@ -317,13 +317,14 @@ static int8_t SCSI_ReadCapacity10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t
UNUSED(params);
int8_t ret;
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
if (hmsc == NULL)
{
return -1;
}
- ret = ((USBD_StorageTypeDef *)pdev->pUserData)->GetCapacity(lun, &hmsc->scsi_blk_nbr, &hmsc->scsi_blk_size);
+ ret = ((USBD_StorageTypeDef *)pdev->pUserData)->GetCapacity(lun, &hlun->nbr, &hlun->size);
if ((ret != 0) || (hmsc->scsi_medium_state == SCSI_MEDIUM_EJECTED))
{
@@ -331,15 +332,15 @@ static int8_t SCSI_ReadCapacity10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t
return -1;
}
- hmsc->bot_data[0] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 24);
- hmsc->bot_data[1] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 16);
- hmsc->bot_data[2] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 8);
- hmsc->bot_data[3] = (uint8_t)(hmsc->scsi_blk_nbr - 1U);
+ hmsc->bot_data[0] = (uint8_t)((hlun->nbr - 1U) >> 24);
+ hmsc->bot_data[1] = (uint8_t)((hlun->nbr - 1U) >> 16);
+ hmsc->bot_data[2] = (uint8_t)((hlun->nbr - 1U) >> 8);
+ hmsc->bot_data[3] = (uint8_t)(hlun->nbr - 1U);
- hmsc->bot_data[4] = (uint8_t)(hmsc->scsi_blk_size >> 24);
- hmsc->bot_data[5] = (uint8_t)(hmsc->scsi_blk_size >> 16);
- hmsc->bot_data[6] = (uint8_t)(hmsc->scsi_blk_size >> 8);
- hmsc->bot_data[7] = (uint8_t)(hmsc->scsi_blk_size);
+ hmsc->bot_data[4] = (uint8_t)(hlun->size >> 24);
+ hmsc->bot_data[5] = (uint8_t)(hlun->size >> 16);
+ hmsc->bot_data[6] = (uint8_t)(hlun->size >> 8);
+ hmsc->bot_data[7] = (uint8_t)(hlun->size);
hmsc->bot_data_length = 8U;
@@ -361,13 +362,14 @@ static int8_t SCSI_ReadCapacity16(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t
uint8_t idx;
int8_t ret;
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
if (hmsc == NULL)
{
return -1;
}
- ret = ((USBD_StorageTypeDef *)pdev->pUserData)->GetCapacity(lun, &hmsc->scsi_blk_nbr, &hmsc->scsi_blk_size);
+ ret = ((USBD_StorageTypeDef *)pdev->pUserData)->GetCapacity(lun, &hlun->nbr, &hlun->size);
if ((ret != 0) || (hmsc->scsi_medium_state == SCSI_MEDIUM_EJECTED))
{
@@ -385,15 +387,15 @@ static int8_t SCSI_ReadCapacity16(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t
hmsc->bot_data[idx] = 0U;
}
- hmsc->bot_data[4] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 24);
- hmsc->bot_data[5] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 16);
- hmsc->bot_data[6] = (uint8_t)((hmsc->scsi_blk_nbr - 1U) >> 8);
- hmsc->bot_data[7] = (uint8_t)(hmsc->scsi_blk_nbr - 1U);
+ hmsc->bot_data[4] = (uint8_t)((hlun->nbr - 1U) >> 24);
+ hmsc->bot_data[5] = (uint8_t)((hlun->nbr - 1U) >> 16);
+ hmsc->bot_data[6] = (uint8_t)((hlun->nbr - 1U) >> 8);
+ hmsc->bot_data[7] = (uint8_t)(hlun->nbr - 1U);
- hmsc->bot_data[8] = (uint8_t)(hmsc->scsi_blk_size >> 24);
- hmsc->bot_data[9] = (uint8_t)(hmsc->scsi_blk_size >> 16);
- hmsc->bot_data[10] = (uint8_t)(hmsc->scsi_blk_size >> 8);
- hmsc->bot_data[11] = (uint8_t)(hmsc->scsi_blk_size);
+ hmsc->bot_data[8] = (uint8_t)(hlun->size >> 24);
+ hmsc->bot_data[9] = (uint8_t)(hlun->size >> 16);
+ hmsc->bot_data[10] = (uint8_t)(hlun->size >> 8);
+ hmsc->bot_data[11] = (uint8_t)(hlun->size);
hmsc->bot_data_length = ((uint32_t)params[10] << 24) |
((uint32_t)params[11] << 16) |
@@ -472,6 +474,12 @@ static int8_t SCSI_ModeSense6(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *pa
{
return -1;
}
+
+ /* Check If media is write-protected */
+ if (((USBD_StorageTypeDef *)pdev->pUserData)->IsWriteProtected(lun) == 1)
+ {
+ MSC_Mode_Sense6_data[2] |= 0x80;
+ }
if (params[4] <= len)
{
@@ -502,6 +510,12 @@ static int8_t SCSI_ModeSense10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *p
return -1;
}
+ /* Check If media is write-protected */
+ if (((USBD_StorageTypeDef *)pdev->pUserData)->IsWriteProtected(lun) == 1)
+ {
+ MSC_Mode_Sense6_data[2] |= 0x80;
+ }
+
if (params[8] <= len)
{
len = params[8];
@@ -688,6 +702,7 @@ static int8_t SCSI_AllowPreventRemovable(USBD_HandleTypeDef *pdev, uint8_t lun,
static int8_t SCSI_Read10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
if (hmsc == NULL)
{
@@ -716,21 +731,20 @@ static int8_t SCSI_Read10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params
return -1;
}
- hmsc->scsi_blk_addr = ((uint32_t)params[2] << 24) |
- ((uint32_t)params[3] << 16) |
- ((uint32_t)params[4] << 8) |
- (uint32_t)params[5];
+ hlun->addr = ((uint32_t)params[2] << 24) |
+ ((uint32_t)params[3] << 16) |
+ ((uint32_t)params[4] << 8) |
+ (uint32_t)params[5];
- hmsc->scsi_blk_len = ((uint32_t)params[7] << 8) | (uint32_t)params[8];
+ hlun->len = ((uint32_t)params[7] << 8) | (uint32_t)params[8];
- if (SCSI_CheckAddressRange(pdev, lun, hmsc->scsi_blk_addr,
- hmsc->scsi_blk_len) < 0)
+ if (SCSI_CheckAddressRange(pdev, lun, hlun->addr, hlun->len) < 0)
{
return -1; /* error */
}
/* cases 4,5 : Hi <> Dn */
- if (hmsc->cbw.dDataLength != (hmsc->scsi_blk_len * hmsc->scsi_blk_size))
+ if (hmsc->cbw.dDataLength != (hlun->len * hlun->size))
{
SCSI_SenseCode(pdev, hmsc->cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB);
return -1;
@@ -754,6 +768,7 @@ static int8_t SCSI_Read10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params
static int8_t SCSI_Read12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
if (hmsc == NULL)
{
@@ -781,24 +796,23 @@ static int8_t SCSI_Read12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params
return -1;
}
- hmsc->scsi_blk_addr = ((uint32_t)params[2] << 24) |
- ((uint32_t)params[3] << 16) |
- ((uint32_t)params[4] << 8) |
- (uint32_t)params[5];
+ hlun->addr = ((uint32_t)params[2] << 24) |
+ ((uint32_t)params[3] << 16) |
+ ((uint32_t)params[4] << 8) |
+ (uint32_t)params[5];
- hmsc->scsi_blk_len = ((uint32_t)params[6] << 24) |
- ((uint32_t)params[7] << 16) |
- ((uint32_t)params[8] << 8) |
- (uint32_t)params[9];
+ hlun->len = ((uint32_t)params[6] << 24) |
+ ((uint32_t)params[7] << 16) |
+ ((uint32_t)params[8] << 8) |
+ (uint32_t)params[9];
- if (SCSI_CheckAddressRange(pdev, lun, hmsc->scsi_blk_addr,
- hmsc->scsi_blk_len) < 0)
+ if (SCSI_CheckAddressRange(pdev, lun, hlun->addr, hlun->len) < 0)
{
return -1; /* error */
}
/* cases 4,5 : Hi <> Dn */
- if (hmsc->cbw.dDataLength != (hmsc->scsi_blk_len * hmsc->scsi_blk_size))
+ if (hmsc->cbw.dDataLength != (hlun->len * hlun->size))
{
SCSI_SenseCode(pdev, hmsc->cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB);
return -1;
@@ -822,6 +836,7 @@ static int8_t SCSI_Read12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params
static int8_t SCSI_Write10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
uint32_t len;
if (hmsc == NULL)
@@ -858,22 +873,21 @@ static int8_t SCSI_Write10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *param
return -1;
}
- hmsc->scsi_blk_addr = ((uint32_t)params[2] << 24) |
- ((uint32_t)params[3] << 16) |
- ((uint32_t)params[4] << 8) |
- (uint32_t)params[5];
+ hlun->addr = ((uint32_t)params[2] << 24) |
+ ((uint32_t)params[3] << 16) |
+ ((uint32_t)params[4] << 8) |
+ (uint32_t)params[5];
- hmsc->scsi_blk_len = ((uint32_t)params[7] << 8) |
- (uint32_t)params[8];
+ hlun->len = ((uint32_t)params[7] << 8) |
+ (uint32_t)params[8];
/* check if LBA address is in the right range */
- if (SCSI_CheckAddressRange(pdev, lun, hmsc->scsi_blk_addr,
- hmsc->scsi_blk_len) < 0)
+ if (SCSI_CheckAddressRange(pdev, lun, hlun->addr, hlun->len) < 0)
{
return -1; /* error */
}
- len = hmsc->scsi_blk_len * hmsc->scsi_blk_size;
+ len = hlun->len * hlun->size;
/* cases 3,11,13 : Hn,Ho <> D0 */
if (hmsc->cbw.dDataLength != len)
@@ -907,6 +921,7 @@ static int8_t SCSI_Write10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *param
static int8_t SCSI_Write12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
uint32_t len;
if (hmsc == NULL)
@@ -945,24 +960,23 @@ static int8_t SCSI_Write12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *param
return -1;
}
- hmsc->scsi_blk_addr = ((uint32_t)params[2] << 24) |
- ((uint32_t)params[3] << 16) |
- ((uint32_t)params[4] << 8) |
- (uint32_t)params[5];
+ hlun->addr = ((uint32_t)params[2] << 24) |
+ ((uint32_t)params[3] << 16) |
+ ((uint32_t)params[4] << 8) |
+ (uint32_t)params[5];
- hmsc->scsi_blk_len = ((uint32_t)params[6] << 24) |
- ((uint32_t)params[7] << 16) |
- ((uint32_t)params[8] << 8) |
- (uint32_t)params[9];
+ hlun->len = ((uint32_t)params[6] << 24) |
+ ((uint32_t)params[7] << 16) |
+ ((uint32_t)params[8] << 8) |
+ (uint32_t)params[9];
/* check if LBA address is in the right range */
- if (SCSI_CheckAddressRange(pdev, lun, hmsc->scsi_blk_addr,
- hmsc->scsi_blk_len) < 0)
+ if (SCSI_CheckAddressRange(pdev, lun, hlun->addr, hlun->len) < 0)
{
return -1; /* error */
}
- len = hmsc->scsi_blk_len * hmsc->scsi_blk_size;
+ len = hlun->len * hlun->size;
/* cases 3,11,13 : Hn,Ho <> D0 */
if (hmsc->cbw.dDataLength != len)
@@ -996,6 +1010,7 @@ static int8_t SCSI_Write12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *param
static int8_t SCSI_Verify10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
if (hmsc == NULL)
{
@@ -1008,7 +1023,7 @@ static int8_t SCSI_Verify10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *para
return -1; /* Error, Verify Mode Not supported*/
}
- if (SCSI_CheckAddressRange(pdev, lun, hmsc->scsi_blk_addr, hmsc->scsi_blk_len) < 0)
+ if (SCSI_CheckAddressRange(pdev, lun, hlun->addr, hlun->len) < 0)
{
return -1; /* error */
}
@@ -1026,22 +1041,23 @@ static int8_t SCSI_Verify10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *para
* @param blk_nbr: number of block to be processed
* @retval status
*/
-static int8_t SCSI_CheckAddressRange(USBD_HandleTypeDef *pdev, uint8_t lun,
+__attribute__((optimize(0))) static int8_t SCSI_CheckAddressRange(USBD_HandleTypeDef *pdev, uint8_t lun,
uint32_t blk_offset, uint32_t blk_nbr)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
-
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
if (hmsc == NULL)
{
return -1;
}
- if ((blk_offset + blk_nbr) > hmsc->scsi_blk_nbr)
+ if ((blk_offset + blk_nbr) > hlun->nbr)
{
SCSI_SenseCode(pdev, lun, ILLEGAL_REQUEST, ADDRESS_OUT_OF_RANGE);
return -1;
}
return 0;
}
@@ -1054,7 +1070,8 @@ static int8_t SCSI_CheckAddressRange(USBD_HandleTypeDef *pdev, uint8_t lun,
static int8_t SCSI_ProcessRead(USBD_HandleTypeDef *pdev, uint8_t lun)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
- uint32_t len = hmsc->scsi_blk_len * hmsc->scsi_blk_size;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
+ uint32_t len = hlun->len * hlun->size;
if (hmsc == NULL)
{
@@ -1064,8 +1081,8 @@ static int8_t SCSI_ProcessRead(USBD_HandleTypeDef *pdev, uint8_t lun)
len = MIN(len, MSC_MEDIA_PACKET);
if (((USBD_StorageTypeDef *)pdev->pUserData)->Read(lun, hmsc->bot_data,
- hmsc->scsi_blk_addr,
- (len / hmsc->scsi_blk_size)) < 0)
+ hlun->addr,
+ (len / hlun->size)) < 0)
{
SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, UNRECOVERED_READ_ERROR);
return -1;
@@ -1073,13 +1090,13 @@ static int8_t SCSI_ProcessRead(USBD_HandleTypeDef *pdev, uint8_t lun)
(void)USBD_LL_Transmit(pdev, MSC_EPIN_ADDR, hmsc->bot_data, len);
- hmsc->scsi_blk_addr += (len / hmsc->scsi_blk_size);
- hmsc->scsi_blk_len -= (len / hmsc->scsi_blk_size);
+ hlun->addr += (len / hlun->size);
+ hlun->len -= (len / hlun->size);
/* case 6 : Hi = Di */
hmsc->csw.dDataResidue -= len;
- if (hmsc->scsi_blk_len == 0U)
+ if (hlun->len == 0U)
{
hmsc->bot_state = USBD_BOT_LAST_DATA_IN;
}
@@ -1096,7 +1113,8 @@ static int8_t SCSI_ProcessRead(USBD_HandleTypeDef *pdev, uint8_t lun)
static int8_t SCSI_ProcessWrite(USBD_HandleTypeDef *pdev, uint8_t lun)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
- uint32_t len = hmsc->scsi_blk_len * hmsc->scsi_blk_size;
+ USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
+ uint32_t len = hlun->len * hlun->size;
if (hmsc == NULL)
{
@@ -1106,26 +1124,26 @@ static int8_t SCSI_ProcessWrite(USBD_HandleTypeDef *pdev, uint8_t lun)
len = MIN(len, MSC_MEDIA_PACKET);
if (((USBD_StorageTypeDef *)pdev->pUserData)->Write(lun, hmsc->bot_data,
- hmsc->scsi_blk_addr,
- (len / hmsc->scsi_blk_size)) < 0)
+ hlun->addr,
+ (len / hlun->size)) < 0)
{
SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, WRITE_FAULT);
return -1;
}
- hmsc->scsi_blk_addr += (len / hmsc->scsi_blk_size);
- hmsc->scsi_blk_len -= (len / hmsc->scsi_blk_size);
+ hlun->addr += (len / hlun->size);
+ hlun->len -= (len / hlun->size);
/* case 12 : Ho = Do */
hmsc->csw.dDataResidue -= len;
- if (hmsc->scsi_blk_len == 0U)
+ if (hlun->len == 0U)
{
MSC_BOT_SendCSW(pdev, USBD_CSW_CMD_PASSED);
}
else
{
- len = MIN((hmsc->scsi_blk_len * hmsc->scsi_blk_size), MSC_MEDIA_PACKET);
+ len = MIN((hlun->len * hlun->size), MSC_MEDIA_PACKET);
/* Prepare EP to Receive next packet */
(void)USBD_LL_PrepareReceive(pdev, MSC_EPOUT_ADDR, hmsc->bot_data, len);
3. Чиним SCSI Mode Sense и добавляем режим Read Only (Write Protect)
При первом знакомстве с ST библиотекой меня очень обрадовало наличие в USB_DEVICE->App->usbd_storage_if.с CALLBACK
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
Но по изучению кода оказалось, что он разве что пригоден для временного отключения записи возвратом USBD_FAIL, а запись включена USBD_OK, тк реализован проверкой результата на момент вызова SCSI Write. Хотелось бы видеть реализацию постоянного запрета на запись при инициализации устройства, о чём сообщает ядро следующим образом (при разрешённой записи):
sd 4:0:0:2: [sdd] Write Protect is off
sd 4:0:0:2: [sdd] Mode Sense: 22 0 0 0
За включение Write Protect отвечает SCSI запрос Mode Sense, возвращающий 4 байта в ответе по спецификации команд SCSI. Но писавшие библиотеку в ST явно чем-то мягким напрыгали на аж 23 байта, ибо 0x22 это длина пакета и это 34! На это пакет WireShark орёт благим матом, только и в лог ядра попадает часть этого безобразия. Ну что же, приводим это недоразумение в файле Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_data.c в соответствии со спецификацией:
/* USB Mass storage sense 6 Data */
uint8_t MSC_Mode_Sense6_data[MODE_SENSE6_LEN] =
{
0x03, /* MODE DATA LENGTH. The number of bytes that follow. */
0x00, /* MEDIUM TYPE. 00h for SBC devices. */
0x00, /* DEVICE-SPECIFIC PARAMETER. For SBC devices:
* bit 7: WP. Set to 1 if the media is write-protected.
* bits 6..4: reserved
* bit 4: DPOFUA. Set to 1 if the device supports the DPO and FUA bits (used in caching)
* bits 3..0: reserved*/
0x00 /* Put Product Serial number */
};
/* USB Mass storage sense 10 Data */
uint8_t MSC_Mode_Sense10_data[MODE_SENSE10_LEN] =
{
0x07, /* MODE DATA LENGTH. The number of bytes that follow. */
0x00, /* MEDIUM TYPE. 00h for SBC devices. */
0x00, /* DEVICE-SPECIFIC PARAMETER. For SBC devices:
* bit 7: WP. Set to 1 if the media is write-protected.
* bits 6..4: reserved
* bit 4: DPOFUA. Set to 1 if the device supports the DPO and FUA bits (used in caching)
* bits 3..0: reserved*/
0x00, /* Reserved */
0x00, /* Reserved */
0x00, /* Reserved */
0x00, /* Reserved */
0x00 /* BLOCK DESCRIPTOR LENGTH. The length in bytes of all block descriptors in the
* mode parameter list. */
};
Не забывая при этом исправить длину ответа в файле Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Inc/usbd_msc_data.h
@@ -40,8 +40,8 @@ extern "C" {
/** @defgroup USB_INFO_Exported_Defines
* @{
*/
-#define MODE_SENSE6_LEN 0x17U
-#define MODE_SENSE10_LEN 0x1BU
+#define MODE_SENSE6_LEN 0x04U
+#define MODE_SENSE10_LEN 0x08U
#define LENGTH_INQUIRY_PAGE00 0x06U
#define LENGTH_INQUIRY_PAGE80 0x08U
Этим мы успокоили WireShak, но мы же хотим сделать ReadOnly. Самый простой вариант и для всех дисков - задать по умочанию прямо в структуре, но он будет сразу для всех LUN. Для выбора задействуем рассмотренный выше CALLBACK STORAGE_IsWriteProtected_FS(uint8_t lun);
, расширив его функциональность. Пусть теперь при возврате USBD_BUSY он делает диск WriteProtect на этапе инициализации. Для этого в файле Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c правим функции обработки SCSI команды SCSI_ModeSense 6 и 10:
@@ -472,6 +472,12 @@ static int8_t SCSI_ModeSense6(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *pa
{
return -1;
}
+
+ /* Check If media is write-protected */
+ if (((USBD_StorageTypeDef *)pdev->pUserData)->IsWriteProtected(lun) == 1)
+ {
+ MSC_Mode_Sense6_data[2] |= 0x80;
+ }
if (params[4] <= len)
{
@@ -502,6 +508,12 @@ static int8_t SCSI_ModeSense10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *p
return -1;
}
+ /* Check If media is write-protected */
+ if (((USBD_StorageTypeDef *)pdev->pUserData)->IsWriteProtected(lun) == 1)
+ {
+ MSC_Mode_Sense6_data[2] |= 0x80;
+ }
+
if (params[8] <= len)
{
len = params[8];
Теперь, при возрате CALLBACK
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
{
/* USER CODE BEGIN 5 */
switch(lun){
case LUN_NUM_MICROSD:
return (USBD_OK);
case LUN_NUM_EEPROM:
return (USBD_BUSY);
break;
case LUN_NUM_SYSTEM:
return (USBD_BUSY);
break;
case LUN_NUM_TEST:
return (USBD_BUSY);
break;
default:
return (USBD_FAIL);
}
/* USER CODE END 5 */
}
Поллучаем для:
LUN_NUM_MICROSD - Запись разрешена
LUN_NUM_EEPROM, LUN_NUM_SYSTEM, LUN_NUM_TEST - Запрещена полностью при инициализации диска системой
Для остальных - Запрещена проверкой в команде SCSI Write
В логе linux выглядит это следующим образом:
sd 4:0:0:0: [sdb] Write Protect is off
sd 4:0:0:0: [sdb] Mode Sense: 03 00 00 00
sd 4:0:0:1: [sdc] Write Protect is on
sd 4:0:0:1: [sdc] Mode Sense: 03 00 80 00
sd 4:0:0:2: [sdd] Write Protect is on
sd 4:0:0:2: [sdd] Mode Sense: 03 00 80 00
sd 4:0:0:3: [sde] Write Protect is on
sd 4:0:0:3: [sde] Mode Sense: 03 00 80 00
sd 4:0:0:4: [sdf] Write Protect is off
sd 4:0:0:4: [sdf] Mode Sense: 03 00 00 00
sd 4:0:0:5: [sdg] Write Protect is off
sd 4:0:0:5: [sdg] Mode Sense: 03 00 00 00
sd 4:0:0:7: [sdi] Write Protect is off
sd 4:0:0:7: [sdi] Mode Sense: 03 00 00 00
sd 4:0:0:6: [sdh] Write Protect is off
sd 4:0:0:6: [sdh] Mode Sense: 03 00 00 00
4. Чиним сброс устройства, добавлением недостающих SCSI команд
После всех предыдущих исправлений осталось единственное. Устройство вроде работает, но в логе сообщений ядра linux проскакивает строчка о сбросе устройства:
usb 1-1: reset full-speed USB device number 10 using xhci_hcd
А в WireShark виден сброс с перезапросом дескрипторов. Этому всегда предшествует некорректный ответ на неизвестные устройству команды. Для исправления этого добавим пустые, синтаксически правильные ответы на следующие команды: SCSI_REPORT_LUNS12, SCSI_LOG_SENSE10, SCSI_RECEIVE_DIAGNOSTIC8, SCSI_ATA_PASSTHROUGH12
Для SCSI_REPORT_LUNS12 добавим номер команды в файл Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Inc/usbd_msc_scsi.h:
@ -74,6 +74,8 @@ extern "C" {
#define SCSI_SEND_DIAGNOSTIC 0x1DU
#define SCSI_READ_FORMAT_CAPACITIES 0x23U
+#define SCSI_REPORT_LUNS12 0xA0U
+
И её реализацию в файл Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c:
@@ -100,6 +100,9 @@ static int8_t SCSI_ProcessWrite(USBD_HandleTypeDef *pdev, uint8_t lun);
static int8_t SCSI_UpdateBotData(USBD_MSC_BOT_HandleTypeDef *hmsc,
uint8_t *pBuff, uint16_t length);
+
+static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef *pdev, uint8_t lun,
+ uint8_t *params);
/**
* @}
*/
@@ -190,6 +193,10 @@ int8_t SCSI_ProcessCmd(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *cmd)
ret = SCSI_Verify10(pdev, lun, cmd);
break;
+ case SCSI_REPORT_LUNS12:
+ ret = SCSI_ReportLuns12(pdev, lun, cmd);
+ break;
+
default:
SCSI_SenseCode(pdev, lun, ILLEGAL_REQUEST, INVALID_CDB);
hmsc->bot_status = USBD_BOT_STATUS_ERROR;
@@ -1041,12 +1048,12 @@ static int8_t SCSI_Verify10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *para
* @param blk_nbr: number of block to be processed
* @retval status
*/
-__attribute__((optimize(0))) static int8_t SCSI_CheckAddressRange(USBD_HandleTypeDef *pdev, uint8_t lun,
+static int8_t SCSI_CheckAddressRange(USBD_HandleTypeDef *pdev, uint8_t lun,
uint32_t blk_offset, uint32_t blk_nbr)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
USBD_LUN_BLK_HandleTypeDef *hlun = &hmsc->scsi_blk[lun];
-/*
+
if (hmsc == NULL)
{
return -1;
@@ -1057,7 +1064,7 @@ __attribute__((optimize(0))) static int8_t SCSI_CheckAddressRange(USBD_HandleTyp
SCSI_SenseCode(pdev, lun, ILLEGAL_REQUEST, ADDRESS_OUT_OF_RANGE);
return -1;
}
-*/
+
return 0;
}
@@ -1181,6 +1188,41 @@ static int8_t SCSI_UpdateBotData(USBD_MSC_BOT_HandleTypeDef *hmsc,
return 0;
}
+
+
+/**
+ * @brief SCSI_Write12
+ * Process Write12 command
+ * @param lun: Logical unit number
+ * @param params: Command parameters
+ * @retval status
+ */
+__attribute__((optimize(0))) static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
+{
+ USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
+
+ uint16_t lun_i;
+
+ static uint64_t report_luns[MSC_BOT_MAX_LUN + 1];
+
+ if (hmsc == NULL)
+ {
+ return -1;
+ }
+
+
+ report_luns[0] = 0;
+ ((uint8_t *)report_luns)[3] = sizeof(uint64_t)*(hmsc->max_lun + 1);
+
+ for (lun_i = 0; lun_i <= hmsc->max_lun; lun_i++){
+ report_luns[lun_i + 1] = lun_i << 8;
+ }
+
+ (void)SCSI_UpdateBotData(hmsc, (uint8_t *)report_luns, sizeof(uint64_t)*(hmsc->max_lun + 2) );
+
+ return 0;
+}
+
/**@@ -75,6 +75,7 @@ extern "C" {
#define SCSI_READ_FORMAT_CAPACITIES 0x23U
#define SCSI_REPORT_LUNS12 0xA0U
+#define SCSI_LOG_SENSE10 0x4DU
Для SCSI_LOG_SENSE10 добавим номер команды в файл Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Inc/usbd_msc_scsi.h:
@@ -75,6 +75,7 @@ extern "C" {
#define SCSI_READ_FORMAT_CAPACITIES 0x23U
#define SCSI_REPORT_LUNS12 0xA0U
+#define SCSI_LOG_SENSE10 0x4DU
И её реализацию в файл Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c:
@@ -103,6 +103,8 @@ static int8_t SCSI_UpdateBotData(USBD_MSC_BOT_HandleTypeDef *hmsc,
static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef *pdev, uint8_t lun,
uint8_t *params);
+static int8_t SCSI_Log_Sense10(USBD_HandleTypeDef *pdev, uint8_t lun,
+ uint8_t *params);
/**
* @}
*/
@@ -197,6 +199,10 @@ int8_t SCSI_ProcessCmd(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *cmd)
ret = SCSI_ReportLuns12(pdev, lun, cmd);
break;
+ case SCSI_LOG_SENSE10:
+ ret = SCSI_Log_Sense10(pdev, lun, cmd);
SCSI_RECEIVE_DIAGNOSTIC8+ break;
+
default:
SCSI_SenseCode(pdev, lun, ILLEGAL_REQUEST, INVALID_CDB);
hmsc->bot_status = USBD_BOT_STATUS_ERROR;
@@ -1197,7 +1203,7 @@ static int8_t SCSI_UpdateBotData(USBD_MSC_BOT_HandleTypeDef *hmsc,
* @param params: Command parameters
* @retval status
*/
-__attribute__((optimize(0))) static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
+static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
@@ -1223,6 +1229,31 @@ __attribute__((optimize(0))) static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef
return 0;
}
+
Для SCSI_RECEIVE_DIAGNOSTIC8, SCSI_ATA_PASSTHROUGH12 добавим номер команды в файл Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Inc/usbd_msc_scsi.h:
@@ -76,6 +76,8 @@ extern "C" {
#define SCSI_REPORT_LUNS12 0xA0U
#define SCSI_LOG_SENSE10 0x4DU
+#define SCSI_RECEIVE_DIAGNOSTIC8 0x1CU
+#define SCSI_ATA_PASSTHROUGH12 0xA1U
И их реализацию в файл Middlewares/ST/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c:
@@ -105,6 +105,11 @@ static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef *pdev, uint8_t lun,
uint8_t *params);
static int8_t SCSI_Log_Sense10(USBD_HandleTypeDef *pdev, uint8_t lun,
uint8_t *params);
+static int8_t SCSI_Recive_Diagnostic8(USBD_HandleTypeDef *pdev, uint8_t lun,
+ uint8_t *params);
+static int8_t SCSI_Ata_PassThrough12(USBD_HandleTypeDef *pdev, uint8_t lun,
+ uint8_t *params);
+
/**
* @}
*/
@@ -203,6 +208,14 @@ int8_t SCSI_ProcessCmd(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *cmd)
ret = SCSI_Log_Sense10(pdev, lun, cmd);
break;
+ case SCSI_RECEIVE_DIAGNOSTIC8:
+ ret = SCSI_Recive_Diagnostic8(pdev, lun, cmd);
+ break;
+
+ case SCSI_ATA_PASSTHROUGH12:
+ ret = SCSI_Ata_PassThrough12(pdev, lun, cmd);
+ break;
+
default:
SCSI_SenseCode(pdev, lun, ILLEGAL_REQUEST, INVALID_CDB);
hmsc->bot_status = USBD_BOT_STATUS_ERROR;
@@ -1238,7 +1251,7 @@ static int8_t SCSI_ReportLuns12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *
* @param params: Command parameters
* @retval status
*/
-__attribute__((optimize(0))) static int8_t SCSI_Log_Sense10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
+static int8_t SCSI_Log_Sense10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
{
USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;
@@ -1254,6 +1267,37 @@ __attribute__((optimize(0))) static int8_t SCSI_Log_Sense10(USBD_HandleTypeDef *
return 0;
}
+
+
+/**
+ * @brief SCSI_Write12
+ * Process Write12 command
+ * @param lun: Logical unit number
+ * @param params: Command parameters
+ * @retval status
+ */
+static int8_t SCSI_Recive_Diagnostic8(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
+{
+
+ return SCSI_Log_Sense10(pdev, lun, params);
+}
+
+
+
+/**
+ * @brief SCSI_Write12
+ * Process Write12 command
+ * @param lun: Logical unit number
+ * @param params: Command parameters
+ * @retval status
+ */
+static int8_t SCSI_Ata_PassThrough12(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params)
+{
+
+ return SCSI_Log_Sense10(pdev, lun, params);
+}
+
+
/**
После всего проделанного видим красивый лог и радуемся корректно работающему устройству:
Log ядра
kernel: [15712.126112] usb 1-1: new full-speed USB device number 73 using xhci_hcd
kernel: [15712.277413] usb 1-1: New USB device found, idVendor=0483, idProduct=572a, bcdDevice= 2.00
kernel: [15712.277419] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
kernel: [15712.277420] usb 1-1: Product: Test MASS Storage
kernel: [15712.277421] usb 1-1: Manufacturer: Developer
kernel: [15712.277422] usb 1-1: SerialNumber: 2062369C5950
kernel: [15712.279576] usb-storage 1-1:1.0: USB Mass Storage device detected
kernel: [15712.279965] scsi host4: usb-storage 1-1:1.0
mtp-probe: checking bus 1 device 73:00:00 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1
mtp-probe: bus: 1 device: 73 was not an MTP device
mtp-probe: checking bus 1 device 73:00:00 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1
mtp-probe: bus: 1 device: 73 was not an MTP device
kernel: [15713.287207] scsi 4:0:0:0: Direct-Access STM MicroSD Device 0.01 PQ: 0 ANSI: 2
kernel: [15713.287882] scsi 4:0:0:1: Direct-Access STM EEPROM 0.01 PQ: 0 ANSI: 2
kernel: [15713.288253] scsi 4:0:0:2: Direct-Access STM System 0.01 PQ: 0 ANSI: 2
kernel: [15713.288611] scsi 4:0:0:3: Direct-Access STM Test 0.01 PQ: 0 ANSI: 2
kernel: [15713.288991] scsi 4:0:0:4: Direct-Access STM MicroSD2Device 0.01 PQ: 0 ANSI: 2
kernel: [15713.289293] scsi 4:0:0:5: Direct-Access STM EEPROM2 0.01 PQ: 0 ANSI: 2
kernel: [15713.289593] scsi 4:0:0:6: Direct-Access STM System2 0.01 PQ: 0 ANSI: 2
kernel: [15713.290194] scsi 4:0:0:7: Direct-Access STM Test2 0.01 PQ: 0 ANSI: 2
kernel: [15713.290496] sd 4:0:0:0: Attached scsi generic sg1 type 0
kernel: [15713.290708] sd 4:0:0:1: Attached scsi generic sg2 type 0
kernel: [15713.290748] sd 4:0:0:0: [sdb] 1030144 512-byte logical blocks: (527 MB/503 MiB)
kernel: [15713.290937] sd 4:0:0:0: [sdb] Write Protect is on
kernel: [15713.290939] sd 4:0:0:2: Attached scsi generic sg3 type 0
kernel: [15713.290939] sd 4:0:0:0: [sdb] Mode Sense: 3 0 80 0
kernel: [15713.291048] sd 4:0:0:3: Attached scsi generic sg4 type 0
kernel: [15713.291117] scsi 4:0:0:4: Attached scsi generic sg5 type 0
kernel: [15713.291147] sd 4:0:0:0: [sdb] No Caching mode page found
kernel: [15713.291149] sd 4:0:0:0: [sdb] Assuming drive cache: write through
kernel: [15713.291194] sd 4:0:0:5: Attached scsi generic sg6kernel: [15712.126112] usb 1-1: new full-speed USB device number 73 using xhci_hcd
kernel: [15712.277413] usb 1-1: New USB device found, idVendor=0483, idProduct=572a, bcdDevice= 2.00
kernel: [15712.277419] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
kernel: [15712.277420] usb 1-1: Product: Test MASS Storage
kernel: [15712.277421] usb 1-1: Manufacturer: Developer
kernel: [15712.277422] usb 1-1: SerialNumber: 2062369C5950
kernel: [15712.279576] usb-storage 1-1:1.0: USB Mass Storage device detected
kernel: [15712.279965] scsi host4: usb-storage 1-1:1.0
mtp-probe: checking bus 1 device 73:00:00 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1
mtp-probe: bus: 1 device: 73 was not an MTP device
mtp-probe: checking bus 1 device 73:00:00 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1
mtp-probe: bus: 1 device: 73 was not an MTP device
kernel: [15713.287207] scsi 4:0:0:0: Direct-Access STM MicroSD Device 0.01 PQ: 0 ANSI: 2
kernel: [15713.287882] scsi 4:0:0:1: Direct-Access STM EEPROM 0.01 PQ: 0 ANSI: 2
kernel: [15713.288253] scsi 4:0:0:2: Direct-Access STM System 0.01 PQ: 0 ANSI: 2
kernel: [15713.288611] scsi 4:0:0:3: Direct-Access STM Test 0.01 PQ: 0 ANSI: 2
kernel: [15713.288991] scsi 4:0:0:4: Direct-Access STM MicroSD2Device 0.01 PQ: 0 ANSI: 2
kernel: [15713.289293] scsi 4:0:0:5: Direct-Access STM EEPROM2 0.01 PQ: 0 ANSI: 2
kernel: [15713.289593] scsi 4:0:0:6: Direct-Access STM System2 0.01 PQ: 0 ANSI: 2
kernel: [15713.290194] scsi 4:0:0:7: Direct-Access STM Test2 0.01 PQ: 0 ANSI: 2
kernel: [15713.290496] sd 4:0:0:0: Attached scsi generic sg1 type 0
kernel: [15713.290708] sd 4:0:0:1: Attached scsi generic sg2 type 0
kernel: [15713.290748] sd 4:0:0:0: [sdb] 1030144 512-byte logical blocks: (527 MB/503 MiB)
kernel: [15713.290937] sd 4:0:0:0: [sdb] Write Protect is on
kernel: [15713.290939] sd 4:0:0:2: Attached scsi generic sg3 type 0
kernel: [15713.290939] sd 4:0:0:0: [sdb] Mode Sense: 3 0 80 0
kernel: [15713.291048] sd 4:0:0:3: Attached scsi generic sg4 type 0
kernel: [15713.291117] scsi 4:0:0:4: Attached scsi generic sg5 type 0
kernel: [15713.291147] sd 4:0:0:0: [sdb] No Caching mode page found
kernel: [15713.291149] sd 4:0:0:0: [sdb] Assuming drive cache: write through
kernel: [15713.291194] sd 4:0:0:5: Attached scsi generic sg6 type 0
kernel: [15713.291274] sd 4:0:0:6: Attached scsi generic sg7 type 0
kernel: [15713.291334] scsi 4:0:0:7: Attached scsi generic sg8 type 0
kernel: [15713.291486] sd 4:0:0:1: [sdc] 64 512-byte logical blocks: (32.8 kB/32.0 KiB)
kernel: [15713.291777] sd 4:0:0:2: [sdd] 256 512-byte logical blocks: (131 kB/128 KiB)
kernel: [15713.291913] sd 4:0:0:1: [sdc] Write Protect is on
kernel: [15713.291915] sd 4:0:0:1: [sdc] Mode Sense: 3 0 80 0
kernel: [15713.292154] sd 4:0:0:3: [sde] 1024 512-byte logical blocks: (524 kB/512 KiB)
kernel: [15713.292299] sd 4:0:0:2: [sdd] Write Protect is on
kernel: [15713.292302] sd 4:0:0:2: [sdd] Mode Sense: 3 0 80 0
kernel: [15713.292444] sd 4:0:0:1: [sdc] No Caching mode page found
kernel: [15713.292446] sd 4:0:0:1: [sdc] Assuming drive cache: write through
kernel: [15713.292792] sd 4:0:0:4: [sdf] 2048 512-byte logical blocks: (1.05 MB/1.00 MiB)
kernel: [15713.292934] sd 4:0:0:3: [sde] Write Protect is on
kernel: [15713.292936] sd 4:0:0:3: [sde] Mode Sense: 3 0 80 0
kernel: [15713.293081] sd 4:0:0:2: [sdd] No Caching mode page found
kernel: [15713.293084] sd 4:0:0:2: [sdd] Assuming drive cache: write through
kernel: [15713.293338] sd 4:0:0:5: [sdg] 4096 512-byte logical blocks: (2.10 MB/2.00 MiB)
kernel: [15713.293483] sd 4:0:0:4: [sdf] Write Protect is on
kernel: [15713.293486] sd 4:0:0:4: [sdf] Mode Sense: 3 0 80 0
kernel: [15713.293642] sd 4:0:0:4: [sdf] No Caching mode page found
kernel: [15713.293644] sd 4:0:0:4: [sdf] Assuming drive cache: write through
kernel: [15713.293794] sd 4:0:0:3: [sde] No Caching mode page found
kernel: [15713.293796] sd 4:0:0:3: [sde] Assuming drive cache: write through
kernel: [15713.294146] sd 4:0:0:6: [sdh] 8192 512-byte logical blocks: (4.19 MB/4.00 MiB)
kernel: [15713.294482] sd 4:0:0:5: [sdg] Write Protect is on
kernel: [15713.294485] sd 4:0:0:5: [sdg] Mode Sense: 3 0 80 0
kernel: [15713.297058] sd 4:0:0:7: [sdi] 16384 512-byte logical blocks: (8.39 MB/8.00 MiB)
kernel: [15713.297215] sd 4:0:0:7: [sdi] Write Protect is on
kernel: [15713.297218] sd 4:0:0:7: [sdi] Mode Sense: 3 0 80 0
kernel: [15713.297374] sd 4:0:0:7: [sdi] No Caching mode page found
kernel: [15713.297376] sd 4:0:0:7: [sdi] Assuming drive cache: write through
kernel: [15713.297550] sd 4:0:0:5: [sdg] No Caching mode page found
kernel: [15713.297553] sd 4:0:0:5: [sdg] Assuming drive cache: write through
kernel: [15713.297788] sd 4:0:0:6: [sdh] Write Protect is on
kernel: [15713.297790] sd 4:0:0:6: [sdh] Mode Sense: 3 0 80 0
kernel: [15713.297972] sd 4:0:0:6: [sdh] No Caching mode page found
kernel: [15713.297974] sd 4:0:0:6: [sdh] Assuming drive cache: write through
kernel: [15713.318373] sdb: sdb1 sdb2 sdb3
kernel: [15713.400473] sd 4:0:0:0: [sdb] Attached SCSI disk
kernel: [15713.461585] sd 4:0:0:2: [sdd] Attached SCSI disk
kernel: [15713.462133] sd 4:0:0:6: [sdh] Attached SCSI disk
kernel: [15713.463092] sd 4:0:0:7: [sdi] Attached SCSI disk
kernel: [15713.486541] sd 4:0:0:3: [sde] Attached SCSI disk
kernel: [15713.494895] sd 4:0:0:4: [sdf] Attached SCSI disk
kernel: [15713.501634] sd 4:0:0:1: [sdc] Attached SCSI disk
kernel: [15713.537123] sd 4:0:0:5: [sdg] Attached SCSI disk
systemd[1]: Finished Clean the /media/user/REC_ROM mount point.
udisksd[1611]: Mounted /dev/sdb2 at /media/user/REC_ROM on behalf of uid 1001
systemd[1788]: Starting Tracker metadata database store and lookup manager...
dbus-daemon[1810]: [session uid=1001 pid=1810] Successfully activated service 'org.freedesktop.Tracker1'
systemd[1788]: Started Tracker metadata database store and lookup manager.
systemd[1]: Finished Clean the /media/user/LAST_ROMS mount point.
udisksd[1611]: Mounted /dev/sdb3 at /media/user/LAST_ROMS on behalf of uid 1001 type 0
kernel: [15713.291274] sd 4:0:0:6: Attached scsi generic sg7 type 0
kernel: [15713.291334] scsi 4:0:0:7: Attached scsi generic sg8 type 0
kernel: [15713.291486] sd 4:0:0:1: [sdc] 64 512-byte logical blocks: (32.8 kB/32.0 KiB)
kernel: [15713.291777] sd 4:0:0:2: [sdd] 256 512-byte logical blocks: (131 kB/128 KiB)
kernel: [15713.291913] sd 4:0:0:1: [sdc] Write Protect is on
kernel: [15713.291915] sd 4:0:0:1: [sdc] Mode Sense: 3 0 80 0
kernel: [15713.292154] sd 4:0:0:3: [sde] 1024 512-byte logical blocks: (524 kB/512 KiB)
kernel: [15713.292299] sd 4:0:0:2: [sdd] Write Protect is on
kernel: [15713.292302] sd 4:0:0:2: [sdd] Mode Sense: 3 0 80 0
kernel: [15713.292444] sd 4:0:0:1: [sdc] No Caching mode page found
kernel: [15713.292446] sd 4:0:0:1: [sdc] Assuming drive cache: write through
kernel: [15713.292792] sd 4:0:0:4: [sdf] 2048 512-byte logical blocks: (1.05 MB/1.00 MiB)
kernel: [15713.292934] sd 4:0:0:3: [sde] Write Protect is on
kernel: [15713.292936] sd 4:0:0:3: [sde] Mode Sense: 3 0 80 0
kernel: [15713.293081] sd 4:0:0:2: [sdd] No Caching mode page found
kernel: [15713.293084] sd 4:0:0:2: [sdd] Assuming drive cache: write through
kernel: [15713.293338] sd 4:0:0:5: [sdg] 4096 512-byte logical blocks: (2.10 MB/2.00 MiB)
kernel: [15713.293483] sd 4:0:0:4: [sdf] Write Protect is on
kernel: [15713.293486] sd 4:0:0:4: [sdf] Mode Sense: 3 0 80 0
kernel: [15713.293642] sd 4:0:0:4: [sdf] No Caching mode page found
kernel: [15713.293644] sd 4:0:0:4: [sdf] Assuming drive cache: write through
kernel: [15713.293794] sd 4:0:0:3: [sde] No Caching mode page found
kernel: [15713.293796] sd 4:0:0:3: [sde] Assuming drive cache: write through
kernel: [15713.294146] sd 4:0:0:6: [sdh] 8192 512-byte logical blocks: (4.19 MB/4.00 MiB)
kernel: [15713.294482] sd 4:0:0:5: [sdg] Write Protect is on
kernel: [15713.294485] sd 4:0:0:5: [sdg] Mode Sense: 3 0 80 0
kernel: [15713.297058] sd 4:0:0:7: [sdi] 16384 512-byte logical blocks: (8.39 MB/8.00 MiB)
kernel: [15713.297215] sd 4:0:0:7: [sdi] Write Protect is on
kernel: [15713.297218] sd 4:0:0:7: [sdi] Mode Sense: 3 0 80 0
kernel: [15713.297374] sd 4:0:0:7: [sdi] No Caching mode page found
kernel: [15713.297376] sd 4:0:0:7: [sdi] Assuming drive cache: write through
kernel: [15713.297550] sd 4:0:0:5: [sdg] No Caching mode page found
kernel: [15713.297553] sd 4:0:0:5: [sdg] Assuming drive cache: write through
kernel: [15713.297788] sd 4:0:0:6: [sdh] Write Protect is on
kernel: [15713.297790] sd 4:0:0:6: [sdh] Mode Sense: 3 0 80 0
kernel: [15713.297972] sd 4:0:0:6: [sdh] No Caching mode page found
kernel: [15713.297974] sd 4:0:0:6: [sdh] Assuming drive cache: write through
kernel: [15713.318373] sdb: sdb1 sdb2 sdb3
kernel: [15713.400473] sd 4:0:0:0: [sdb] Attached SCSI disk
kernel: [15713.461585] sd 4:0:0:2: [sdd] Attached SCSI disk
kernel: [15713.462133] sd 4:0:0:6: [sdh] Attached SCSI disk
kernel: [15713.463092] sd 4:0:0:7: [sdi] Attached SCSI disk
kernel: [15713.486541] sd 4:0:0:3: [sde] Attached SCSI disk
kernel: [15713.494895] sd 4:0:0:4: [sdf] Attached SCSI disk
kernel: [15713.501634] sd 4:0:0:1: [sdc] Attached SCSI disk
kernel: [15713.537123] sd 4:0:0:5: [sdg] Attached SCSI disk
systemd[1]: Finished Clean the /media/user/REC_ROM mount point.
udisksd[1611]: Mounted /dev/sdb2 at /media/user/REC_ROM on behalf of uid 1001
systemd[1788]: Starting Tracker metadata database store and lookup manager...
dbus-daemon[1810]: [session uid=1001 pid=1810] Successfully activated service 'org.freedesktop.Tracker1'
systemd[1788]: Started Tracker metadata database store and lookup manager.
systemd[1]: Finished Clean the /media/user/LAST_ROMS mount point.
udisksd[1611]: Mounted /dev/sdb3 at /media/user/LAST_ROMS on behalf of uid 1001
Позапрашиваем информацию о LUN
Выведем все SCSI устройства:
# sg_map -x
/dev/sg0 0 0 0 0 0 /dev/sda
/dev/sg1 4 0 0 0 0 /dev/sdb
/dev/sg2 4 0 0 1 0 /dev/sdc
/dev/sg3 4 0 0 2 0 /dev/sdd
/dev/sg4 4 0 0 3 0 /dev/sde
/dev/sg5 4 0 0 4 0 /dev/sdf
/dev/sg6 4 0 0 5 0 /dev/sdg
/dev/sg7 4 0 0 6 0 /dev/sdh
/dev/sg8 4 0 0 7 0 /dev/sdi
Запросим ёмкость:
# sg_readcap /dev/sg1
Read Capacity results:
Last LBA=1030143 (0xfb7ff), Number of logical blocks=1030144
Logical block length=512 bytes
Hence:
Device size: 527433728 bytes, 503.0 MiB, 0.53 GB
# sg_readcap /dev/sg2
Read Capacity results:
Last LBA=63 (0x3f), Number of logical blocks=64
Logical block length=512 bytes
Hence:
Device size: 32768 bytes, 0.0 MiB, 0.00 GB
# sg_readcap /dev/sg3
Read Capacity results:
Last LBA=255 (0xff), Number of logical blocks=256
Logical block length=512 bytes
Hence:
Device size: 131072 bytes, 0.1 MiB, 0.00 GB
# sg_readcap /dev/sg4
Read Capacity results:
Last LBA=1023 (0x3ff), Number of logical blocks=1024
Logical block length=512 bytes
Hence:
Device size: 524288 bytes, 0.5 MiB, 0.00 GB
# sg_readcap /dev/sg5
Read Capacity results:
Last LBA=2047 (0x7ff), Number of logical blocks=2048
Logical block length=512 bytes
Hence:
Device size: 1048576 bytes, 1.0 MiB, 0.00 GB
# sg_readcap /dev/sg6
Read Capacity results:
Last LBA=4095 (0xfff), Number of logical blocks=4096
Logical block length=512 bytes
Hence:
Device size: 2097152 bytes, 2.0 MiB, 0.00 GB
# sg_readcap /dev/sg7
Read Capacity results:
Last LBA=8191 (0x1fff), Number of logical blocks=8192
Logical block length=512 bytes
Hence:
Device size: 4194304 bytes, 4.0 MiB, 0.00 GB
# sg_readcap /dev/sg8
Read Capacity results:
Last LBA=16383 (0x3fff), Number of logical blocks=16384
Logical block length=512 bytes
Hence:
Device size: 8388608 bytes, 8.0 MiB, 0.01 GB
Прочитаем метки:
# sg_raw -r 1k /dev/sg1 12 00 00 00 60 00
SCSI Status: Good
Received 36 bytes of data:
00 00 00 02 02 1f 00 00 00 53 54 4d 20 20 20 20 20 ........STM
10 4d 69 63 72 6f 53 44 20 44 65 76 69 63 65 20 20 MicroSD Device
20 30 2e 30 31 0.01
# sg_raw -r 1k /dev/sg2 12 00 00 00 60 00
SCSI Status: Good
Received 36 bytes of data:
00 00 00 02 02 1f 00 00 00 53 54 4d 20 20 20 20 20 ........STM
10 45 45 50 52 4f 4d 20 20 20 20 20 20 20 20 20 20 EEPROM
20 30 2e 30 31 0.01
# sg_raw -r 1k /dev/sg3 12 00 00 00 60 00
SCSI Status: Good
Received 36 bytes of data:
00 00 00 02 02 1f 00 00 00 53 54 4d 20 20 20 20 20 ........STM
10 53 79 73 74 65 6d 20 20 20 20 20 20 20 20 20 20 System
20 30 2e 30 31 0.01
# sg_raw -r 1k /dev/sg4 12 00 00 00 60 00
SCSI Status: Good
Received 36 bytes of data:
00 00 00 02 02 1f 00 00 00 53 54 4d 20 20 20 20 20 ........STM
10 54 65 73 74 20 20 20 20 20 20 20 20 20 20 20 20 Test
20 30 2e 30 31
Выведем списком все LUN в узле:
# sg_luns /dev/sg3
Lun list length = 64 which imples 8 lun entries
Report luns [select_report=0x0]:
0000000000000000
0001000000000000
0002000000000000
0003000000000000
0004000000000000
0005000000000000
0006000000000000
0007000000000000
Запросим логи LUN:
# sg_logs /dev/sg1
STM MicroSD Device 0.01
# sg_logs /dev/sg2
STM EEPROM 0.01
# sg_logs /dev/sg3
STM System 0.01
# sg_logs /dev/sg4
STM Test 0.01
# sg_logs /dev/sg5
STM MicroSD2Device 0.01
# sg_logs /dev/sg6
STM EEPROM2 0.01
# sg_logs /dev/sg7
STM System2 0.01
# sg_logs /dev/sg8
STM Test2 0.01
Почитаем данные:
#xxd -l 0x200 -s 0x200 /dev/sdb
00000200: ebfe 904d 5344 4f53 352e 3000 0201 0100 ...MSDOS5.0.....
00000210: 0140 0010 02f8 0200 3f00 ff00 0100 0000 .@......?.......
00000220: 0000 0000 8001 29ba 97e1 524e 4f20 4e41 ......)...RNO NA
00000230: 4d45 2020 2020 4641 5420 2020 2020 0000 ME FAT ..
00000240: 0000 0000 0000 0000 0000 0000 0000 0000 ................
...................................................................
000003e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000003f0: 0000 0000 0000 0000 0000 0000 0000 55aa ..............U.
#xxd -l 0x100 -s 0x800 /dev/sdc
00000800: 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f ................
00000810: 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f ................
00000820: 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f !"#$%&'()*+,-./
00000830: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
00000840: 4041 4243 4445 4647 4849 4a4b 4c4d 4e4f @ABCDEFGHIJKLMNO
00000850: 5051 5253 5455 5657 5859 5a5b 5c5d 5e5f PQRSTUVWXYZ[]^_
00000860: 6061 6263 6465 6667 6869 6a6b 6c6d 6e6f `abcdefghijklmno
00000870: 7071 7273 7475 7677 7879 7a7b 7c7d 7e7f pqrstuvwxyz{|}~.
00000880: 8081 8283 8485 8687 8889 8a8b 8c8d 8e8f ................
00000890: 9091 9293 9495 9697 9899 9a9b 9c9d 9e9f ................
000008a0: a0a1 a2a3 a4a5 a6a7 a8a9 aaab acad aeaf ................
000008b0: b0b1 b2b3 b4b5 b6b7 b8b9 babb bcbd bebf ................
000008c0: c0c1 c2c3 c4c5 c6c7 c8c9 cacb cccd cecf ................
000008d0: d0d1 d2d3 d4d5 d6d7 d8d9 dadb dcdd dedf ................
000008e0: e0e1 e2e3 e4e5 e6e7 e8e9 eaeb eced eeef ................
000008f0: f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff ................
# xxd -l 0x20 -s 0x800 /dev/sdd
00000800: 0404 0404 0404 0404 0404 0404 0404 0404 ................
00000810: 0404 0404 0404 0404 0404 0404 0404 0404 ................
Интеграция с CubeMX / CubeIDE
Если после всех исправлений, попытаться перегенерировать проек кубом, то все правки, за исключением USB_DEVICE->*** будут откатаны к оригинальной библиотеке. Чтобы этого не происходило и при создании новых проектов брались уже испавленные версии, необходимо заменить файлы не только в локальной копии, но и в репозитории библиотек, откуда их берёт Cube. В Linux это папка ~/STM32Cube/Repository/STM32Cube_FW_L4_V1.17.0/Middlewares/ST/STM32_USB_Device_Library/Class/MSC.
И на последок исправленная библиотека на GitHub: https://github.com/Igorbunow/STM32CubeL4/tree/testing ветка testing!!!
По обнаруженным ошибкам сделан Pull Request в официальный репозиторий ST, надеюсь через какое-то время они поправят у себя для всех веток контроллера, а не только L4. Для остальных семейств не смотрел, но отличия в правках должны быть минимальны
Используемые материалы:
[1] Jan Axelson USB Mass Storage Designing and Programming Devices and Embedded Hosts
[2] Microchip AN2554 https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/00002554A.pdf
[3] Microchip AN1169 http://www.t-es-t.hu/download/microchip/an1169a.pdf
[4] ST UM1717, UM0424, UM1021, UM1720
[5] Universal Serial Bus Mass Storage Class Bulk-Only Transport https://www.usb.org/sites/default/files/usbmassbulk_10.pdf
[6] SCSI Commands Reference Manual Seagate https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
[7] https://habr.com/ru/post/549016/
Автор: Игорь