cancel
Showing results for 
Search instead for 
Did you mean: 

USB Host support for devices with more than one logical unit

jdcowpland
Associate II
Posted on February 26, 2014 at 22:11

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-host
10 REPLIES 10
tsuneo
Senior
Posted on March 01, 2014 at 16:34

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
jdcowpland
Associate II
Posted on March 28, 2014 at 16:09

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.maxLun

2--> Modify scsi commands to take in LUN

3--> 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 commands

7--> Modify disk_ioctl to load correct param structure for lun

If 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!

tsuneo
Senior
Posted on March 28, 2014 at 21:33

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

Tsuneo

jdcowpland
Associate II
Posted on April 01, 2014 at 12:16

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?

tsuneo
Senior
Posted on April 01, 2014 at 21:39

> 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.

Tsuneo

jdcowpland
Associate II
Posted on April 02, 2014 at 16:59

It's happening with the first LUN.

Just discovered though that the device doesn't have an MBR but has a super floppy instead.

tsuneo
Senior
Posted on April 02, 2014 at 20:38

> 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
jdcowpland
Associate II
Posted on April 03, 2014 at 10:07

Tried your suggestion and it doesn't seem to have made any difference. Is there anything else it could be?

tsuneo
Senior
Posted on April 06, 2014 at 19:21

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