2014-02-26 01:11 PM
Anyone know if it's easy to edit the ST USB host library so that it supports flash drives made up of more than one logical unit? Or if there's another library out there that does?
#usb-msc-host2014-03-01 07:34 AM
As you are asking this question, I believe the job is hard for you ;)
I hope this brief explanation helps you. 1) Initialization of each LUN Just after enumeration of the target device by the stack, the host MSC code initializes the logical units on the device, repeatedly calling USBH_MSC_Handle(). Get_Max_Lun is the second request to the device, after BOT Reset.usbh_msc_core.c
static USBH_Status USBH_MSC_Handle(USB_OTG_CORE_HANDLE *pdev ,
void *phost)
{
...
case USBH_MSC_GET_MAX_LUN:
/* Issue GetMaxLUN request */
status = USBH_MSC_GETMaxLUN(pdev, phost);
if(status == USBH_OK )
{
MSC_Machine.maxLun = *(MSC_Machine.buff) ;
/* If device has more that one logical unit then it is not supported */
if((MSC_Machine.maxLun > 0) && (maxLunExceed == FALSE))
{
maxLunExceed = TRUE;
pphost->usr_cb->DeviceNotSupported();
break;
}
USBH_MSC_BOTXferParam.MSCState = USBH_MSC_TEST_UNIT_READY;
}
In this MSC code, the max LUN (1, in your case) is stored to MSC_Machine.maxLun.
- as LUN starts with 0, two LUNs results in LUN 0 and LUN 1.
The code ''if((MSC_Machine.maxLun > 0)'' checks this number, and the stack rejects further proces.
This is the first point you have to modify.
After this code, the state machine in USBH_MSC_Handle() traverses these cases for single LUN.
- case USBH_MSC_TEST_UNIT_READY:
- case USBH_MSC_READ_CAPACITY10:
- case USBH_MSC_MODE_SENSE6:
You have to repeat these states for the second LUN.
The LUN is passed to the SCSI command calls as an additional parameter.
For example,
USBH_MSC_TestUnitReady(pdev) --> USBH_MSC_TestUnitReady(pdev, 0) // for LUN 0
The implementation is,
usbh_msc_scsi.c
uint8_t USBH_MSC_TestUnitReady (USB_OTG_CORE_HANDLE *pdev, uint8_t lun) // with additional ''lun''
{
uint8_t index;
USBH_MSC_Status_TypeDef status = USBH_MSC_BUSY;
if(HCD_IsDeviceConnected(pdev))
{
switch(USBH_MSC_BOTXferParam.CmdStateMachine)
{
case CMD_SEND_STATE:
/*Prepare the CBW and relevent field*/
USBH_MSC_CBWData.field.CBWLUN = lun; // <----------
USBH_MSC_CBWData.field.CBWTransferLength = 0; /* No Data Transfer */
...
Each SCSI command call in usbh_msc_scsi.c should be modified in this way.
Also, a couple of SCSI command calls hold the result in USBH_MSC_Param structure.
Each LUN should have separate parameters.
typedef struct __MassStorageParameter
{
uint32_t MSCapacity; <----
uint32_t MSSenseKey; <----
uint16_t MSPageLength; <----
uint8_t MSBulkOutEp;
uint8_t MSBulkInEp;
uint8_t MSWriteProtect; <----
} MassStorageParameter_TypeDef;
2) Expose two drives over FatFs
All of subroutines in usbh_msc_fatfs.c should be modified, so that they accept drive number (1)
- disk_read() and disk_write() pass the drive number to the SCSI commands,
USBH_MSC_Read10() and USBH_MSC_Write10(), respectively, as the lun parameter.
- disk_ioctl(GET_SECTOR_COUNT) returns each USBH_MSC_Param.MSCapacity of the target drive (lun)
Now that you may use the second driver over FatFs.
I believe I've covered most of the modifications.
Tsuneo
2014-03-28 08:09 AM
Ok thanks! Seems like some pretty serious changes so I just want to make sure I've understood everything before I go and do it. This is how I understand what you said:
1--> Remove check of MSC_Machine.maxLun2--> Modify scsi commands to take in LUN3--> Modify each scsi command by adding line: USBH_MSC_CBWData.field.CBWLUN = lun;4--> Create Param structures for each LUN and make sure each scsi command uses the structure needed for the lun called.5--> Modify the USBH_MSC_Handle function so that the state machine repeats states USBH_MSC_TEST_UNIT_READY, USBH_MSC_READ_CAPACITY10, USBH_MSC_MODE_SENSE6.6--> Modify fatfs code to allow passing of lun as a drive number to scsi commands7--> Modify disk_ioctl to load correct param structure for lunIf that all looks alright, then great. The only thing I'm not sure how to do is part 4 with the state machine. What's the best way to go about doing that?Cheers!2014-03-28 01:33 PM
Fine.
Here is additional suggestions, 1--> Remove check of MSC_Machine.maxLun Define a constant, which stands for number of LUNs supported in your implementation. For example, #define MAX_LOGICAL_UNITS 2 And then, ''if((MSC_Machine.maxLun > 0) && (maxLunExceed == FALSE))'' is modified as,if((MSC_Machine.maxLun >= MAX_LOGICAL_UNITS) && (maxLunExceed == FALSE))
4--> Create Param structures for each LUN and make sure each scsi command uses the structure needed for the lun called. The structure definition is altered as,\STM32_USB-Host-Device_Lib_V2.1.0\Libraries\STM32_USB_HOST_Library\Class\MSC\inc\usbh_msc_scsi.h
typedef struct __MassStorageParameter { uint32_t MSCapacity[ MAX_LOGICAL_UNITS ]; uint32_t MSSenseKey[ MAX_LOGICAL_UNITS ]; uint16_t MSPageLength[ MAX_LOGICAL_UNITS ]; uint8_t MSBulkOutEp; uint8_t MSBulkInEp; uint8_t MSWriteProtect[ MAX_LOGICAL_UNITS ]; } MassStorageParameter_TypeDef; > The only thing I'm not sure how to do is part 4 with the state machine. 5--> Modify the USBH_MSC_Handle function so that the state machine repeats states\STM32_USB-Host-Device_Lib_V2.1.0\Libraries\STM32_USB_HOST_Library\Class\MSC\src\usbh_msc_core.c
static USBH_Status USBH_MSC_Handle(USB_OTG_CORE_HANDLE *pdev , void *phost) { static uint8_t lun = 0; // <------- (1) ... case USBH_MSC_GET_MAX_LUN: ... ... lun = 0; // <------- (2) USBH_MSC_BOTXferParam.MSCState = USBH_MSC_TEST_UNIT_READY; } ... ... break; case USBH_MSC_TEST_UNIT_READY: /* Issue SCSI command TestUnitReady */ mscStatus = USBH_MSC_TestUnitReady(pdev, lun); // <------- (3) ... break; case USBH_MSC_READ_CAPACITY10: /* Issue READ_CAPACITY10 SCSI command */ mscStatus = USBH_MSC_ReadCapacity10(pdev, lun); // <------- (3) ... break; case USBH_MSC_MODE_SENSE6: /* Issue ModeSense6 SCSI command for detecting if device is write-protected */ mscStatus = USBH_MSC_ModeSense6(pdev, lun); // <------- (3) if(mscStatus == USBH_MSC_OK ) { if (lun < MSC_Machine.maxLun ) // <------- (4) { lun++: USBH_MSC_BOTXferParam.MSCState = USBH_MSC_TEST_UNIT_READY; } else { USBH_MSC_BOTXferParam.MSCState = USBH_MSC_DEFAULT_APPLI_STATE; } MSCErrorCount = 0; status = USBH_OK; } ... (1) declare static lun variable (2) initialize lun variable (3) add ''lun'' parameter to each SCSI command (4) increase lun and get back to USBH_MSC_TEST_UNIT_READY, to travers all luns Tsuneo2014-04-01 03:16 AM
Thanks Tsuneo, really helpful! I've made the changes you suggested and my library is still working with single lun devices, but with my 2 lun device, it's constantly replying with a mscStatus = USBH_MSC_BUSY when it tries the testunitready command. Any idea what might be going on?
2014-04-01 12:39 PM
> but with my 2 lun device, it's constantly replying with a mscStatus = USBH_MSC_BUSY when it tries the testunitready command.
Is it on the first LUN or the second? What is the target device? Some multi-LUN devices require extra ''command'' to mount the second LUN, such as secure USB disk, ZERO-CD modem, etc. Tsuneo2014-04-02 07:59 AM
It's happening with the first LUN.
Just discovered though that the device doesn't have an MBR but has a super floppy instead.2014-04-02 11:38 AM
> It's happening with the first LUN.
The multi-LUN device seems not to like the sequence of ST's MSC host. Try Windows sequence. Windows doesn't put USBH_MSC_BOT_RESET stage, before USBH_MSC_GET_MAX_LUN Skip USBH_MSC_BOT_RESET stage in USBH_MSC_Handle()\STM32_USB-Host-Device_Lib_V2.1.0\Libraries\STM32_USB_HOST_Library\Class\MSC\src\usbh_msc_core.c
static USBH_Status USBH_MSC_Handle(USB_OTG_CORE_HANDLE *pdev ,
void *phost)
{
...
switch(USBH_MSC_BOTXferParam.MSCState)
{
case USBH_MSC_BOT_INIT_STATE:
USBH_MSC_Init(pdev);
// USBH_MSC_BOTXferParam.MSCState = USBH_MSC_BOT_RESET; // <---- skip BOT Reset
USBH_MSC_BOTXferParam.MSCState = USBH_MSC_GET_MAX_LUN; // <----
break;
The multi-LUN device may implement BOT Reset wrongly,
it may expect Set_Feature(ENDPOINT_HALT) on the bulk IN/OUT endpoints after BOT Reset.
> Just discovered though that the device doesn't have an MBR but has a super floppy instead.
The FatFs version on the STM32_USB-Host-Device_Lib_V2.1.0 support SFD (Super Floppy Disk) format, too.
Tsuneo
2014-04-03 01:07 AM
Tried your suggestion and it doesn't seem to have made any difference. Is there anything else it could be?
2014-04-06 10:21 AM
The multi-LUN device should be slightly out of standard.
We have to know how it deviates from the standard, with successful enumeration. Do Windows (or Linux) mount the multi-LUN device successfully ? Monitor USB traffic of enumeration of this device, using a sniffer. Compare the trace with those of other device (single-LUN). You may use these sniffers for this purpose, USBlyzer (commercial, 33 days trial) http://www.usblyzer.com/ USBTrace (commercial, 15 days trial) http://www.sysnucleus.com/ To catch enumeration on USBlyzer, you'll need to enable ''Capture menu -> Capture Hot-plugged'' before plugging in the target printer to the PC. Tsuneo