forked from xuos/xiuos
571 lines
13 KiB
C
571 lines
13 KiB
C
/*
|
|
* Copyright (c) 2006-2018, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2011-12-12 Yi Qiu first version
|
|
*/
|
|
|
|
/**
|
|
* @file mass.c
|
|
* @brief support usb storage function
|
|
* @version 1.0
|
|
* @author AIIT XUOS Lab
|
|
* @date 2021-04-25
|
|
*/
|
|
|
|
/*************************************************
|
|
File name: mass.c
|
|
Description: support usb storage function
|
|
Others: take RT-Thread v4.0.2/components/drivers/usb/usbhost/class/mass.c for references
|
|
https://github.com/RT-Thread/rt-thread/tree/v4.0.2
|
|
History:
|
|
1. Date: 2021-04-25
|
|
Author: AIIT XUOS Lab
|
|
Modification:
|
|
1. support usb storage configure, function and struct
|
|
2. support usb bus driver framework
|
|
*************************************************/
|
|
|
|
#include <xiuos.h>
|
|
#include <usb_host.h>
|
|
#include "mass.h"
|
|
|
|
#ifdef USBH_MSTORAGE
|
|
|
|
extern x_err_t UdiskRun(struct uhintf* intf);
|
|
extern x_err_t UdiskStop(struct uhintf* intf);
|
|
|
|
static struct UclassDriver StorageDriver;
|
|
|
|
|
|
static x_err_t PipeCheck(struct uhintf* intf, upipe_t pipe)
|
|
{
|
|
struct uinstance* device;
|
|
x_err_t ret;
|
|
UstorPointer stor;
|
|
int size = 0;
|
|
struct UstorageCsw csw;
|
|
|
|
if(intf == NONE || pipe == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
|
|
device = intf->device;
|
|
|
|
|
|
stor = (UstorPointer)intf->UserData;
|
|
|
|
|
|
if(pipe->status == UPIPE_STATUS_OK) return EOK;
|
|
|
|
if(pipe->status == UPIPE_STATUS_ERROR)
|
|
{
|
|
KPrintf("pipe status error\n");
|
|
return -EPIO;
|
|
}
|
|
if(pipe->status == UPIPE_STATUS_STALL)
|
|
{
|
|
|
|
ret = UsbhClearFeature(device, pipe->ep.bEndpointAddress,
|
|
USB_FEATURE_ENDPOINT_HALT);
|
|
if(ret != EOK) return ret;
|
|
}
|
|
|
|
|
|
DelayKTask(50);
|
|
|
|
KPrintf("pipes1 0x%x, 0x%x\n", stor->pipe_in, stor->pipe_out);
|
|
|
|
stor->pipe_in->status = UPIPE_STATUS_OK;
|
|
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("clean storage in pipe stall\n"));
|
|
|
|
|
|
size = UsbHcdPipeXfer(stor->pipe_in->inst->hcd,
|
|
stor->pipe_in, &csw, SIZEOF_CSW, 100);
|
|
if(size != SIZEOF_CSW)
|
|
{
|
|
KPrintf("receive the csw after stall failed\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
return -ERROR;
|
|
}
|
|
|
|
|
|
static x_err_t UsbBulkOnlyXfer(struct uhintf* intf,
|
|
UstorageCbwPointer cmd, uint8* buffer, int timeout)
|
|
{
|
|
x_size_t size;
|
|
x_err_t ret;
|
|
upipe_t pipe;
|
|
struct UstorageCsw csw;
|
|
UstorPointer stor;
|
|
|
|
NULL_PARAM_CHECK(cmd);
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
|
|
stor = (UstorPointer)intf->UserData;
|
|
|
|
do
|
|
{
|
|
|
|
size = UsbHcdPipeXfer(stor->pipe_out->inst->hcd, stor->pipe_out,
|
|
cmd, SIZEOF_CBW, timeout);
|
|
if(size != SIZEOF_CBW)
|
|
{
|
|
KPrintf("CBW size error\n");
|
|
return -EPIO;
|
|
}
|
|
if(cmd->XferLen != 0)
|
|
{
|
|
pipe = (cmd->dflags == CBWFLAGS_DIR_IN) ? stor->pipe_in :
|
|
stor->pipe_out;
|
|
size = UsbHcdPipeXfer(pipe->inst->hcd, pipe, (void*)buffer,
|
|
cmd->XferLen, timeout);
|
|
if(size != cmd->XferLen)
|
|
{
|
|
KPrintf("request size %d, transfer size %d\n",
|
|
cmd->XferLen, size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
size = UsbHcdPipeXfer(stor->pipe_in->inst->hcd, stor->pipe_in,
|
|
&csw, SIZEOF_CSW, timeout);
|
|
if(size != SIZEOF_CSW)
|
|
{
|
|
KPrintf("csw size error\n");
|
|
return -EPIO;
|
|
}
|
|
}while(0);
|
|
|
|
|
|
ret = PipeCheck(intf, stor->pipe_in);
|
|
if(ret != EOK)
|
|
{
|
|
KPrintf("in pipe error\n");
|
|
return ret;
|
|
}
|
|
|
|
|
|
ret = PipeCheck(intf, stor->pipe_out);
|
|
if(ret != EOK)
|
|
{
|
|
KPrintf("out pipe error\n");
|
|
return ret;
|
|
}
|
|
|
|
|
|
if(csw.signature != CSW_SIGNATURE || csw.tag != CBW_TAG_VALUE)
|
|
{
|
|
KPrintf("csw signature error\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
if(csw.status != 0)
|
|
{
|
|
|
|
return -ERROR;
|
|
}
|
|
|
|
return EOK;
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageGetMaxLun(struct uhintf* intf, uint8* MaxLun)
|
|
{
|
|
struct uinstance* device;
|
|
struct urequest setup;
|
|
int timeout = USB_TIMEOUT_BASIC;
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageGetMaxLun\n"));
|
|
|
|
|
|
device = intf->device;
|
|
|
|
|
|
setup.RequestType = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS |
|
|
USB_REQ_TYPE_INTERFACE;
|
|
setup.bRequest = USBREQ_GET_MAX_LUN;
|
|
setup.wValue = intf->IntfDesc->bInterfaceNumber;
|
|
setup.wIndex = 0;
|
|
setup.wLength = 1;
|
|
|
|
|
|
if(UsbHcdSetupXfer(device->hcd, device->PipeEp0Out, &setup, timeout) != 8)
|
|
{
|
|
return -EPIO;
|
|
}
|
|
if(UsbHcdPipeXfer(device->hcd, device->PipeEp0In, MaxLun, 1, timeout) != 1)
|
|
{
|
|
return -EPIO;
|
|
}
|
|
if(UsbHcdPipeXfer(device->hcd, device->PipeEp0Out, NONE, 0, timeout) != 0)
|
|
{
|
|
return -EPIO;
|
|
}
|
|
return EOK;
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageReset(struct uhintf* intf)
|
|
{
|
|
struct urequest setup;
|
|
struct uinstance* device;
|
|
int timeout = USB_TIMEOUT_BASIC;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageReset\n"));
|
|
|
|
|
|
device = intf->device;
|
|
|
|
|
|
setup.RequestType = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS |
|
|
USB_REQ_TYPE_INTERFACE;
|
|
setup.bRequest = USBREQ_MASS_STORAGE_RESET;
|
|
setup.wIndex = intf->IntfDesc->bInterfaceNumber;
|
|
setup.wLength = 0;
|
|
setup.wValue = 0;
|
|
|
|
if(UsbHcdSetupXfer(device->hcd, device->PipeEp0Out, &setup, timeout) != 8)
|
|
{
|
|
return -EPIO;
|
|
}
|
|
if(UsbHcdPipeXfer(device->hcd, device->PipeEp0In, NONE, 0, timeout) != 0)
|
|
{
|
|
return -EPIO;
|
|
}
|
|
return EOK;
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageRead10(struct uhintf* intf, uint8 *buffer,
|
|
uint32 sector, x_size_t count, int timeout)
|
|
{
|
|
struct UstorageCbw cmd;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageRead10\n"));
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct UstorageCbw));
|
|
cmd.signature = CBW_SIGNATURE;
|
|
cmd.tag = CBW_TAG_VALUE;
|
|
cmd.XferLen = SECTOR_SIZE * count;
|
|
cmd.dflags = CBWFLAGS_DIR_IN;
|
|
cmd.lun = 0;
|
|
cmd.CbLen = 10;
|
|
cmd.cb[0] = SCSI_READ_10;
|
|
cmd.cb[1] = 0;
|
|
cmd.cb[2] = (uint8)(sector >> 24);
|
|
cmd.cb[3] = (uint8)(sector >> 16);
|
|
cmd.cb[4] = (uint8)(sector >> 8);
|
|
cmd.cb[5] = (uint8)sector;
|
|
cmd.cb[6] = 0;
|
|
cmd.cb[7] = (count & 0xff00) >> 8;
|
|
cmd.cb[8] = (uint8) count & 0xff;
|
|
|
|
return UsbBulkOnlyXfer(intf, &cmd, buffer, timeout);
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageWrite10(struct uhintf* intf, uint8 *buffer,
|
|
uint32 sector, x_size_t count, int timeout)
|
|
{
|
|
struct UstorageCbw cmd;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageWrite10\n"));
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct UstorageCbw));
|
|
cmd.signature = CBW_SIGNATURE;
|
|
cmd.tag = CBW_TAG_VALUE;
|
|
cmd.XferLen = SECTOR_SIZE * count;
|
|
cmd.dflags = CBWFLAGS_DIR_OUT;
|
|
cmd.lun = 0;
|
|
cmd.CbLen = 10;
|
|
cmd.cb[0] = SCSI_WRITE_10;
|
|
cmd.cb[1] = 0;
|
|
cmd.cb[2] = (uint8)(sector >> 24);
|
|
cmd.cb[3] = (uint8)(sector >> 16);
|
|
cmd.cb[4] = (uint8)(sector >> 8);
|
|
cmd.cb[5] = (uint8)sector;
|
|
cmd.cb[6] = 0;
|
|
cmd.cb[7] = (count & 0xff00) >> 8;
|
|
cmd.cb[8] = (uint8) count & 0xff;
|
|
|
|
return UsbBulkOnlyXfer(intf, &cmd, buffer, timeout);
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageRequestSense(struct uhintf* intf, uint8* buffer)
|
|
{
|
|
struct UstorageCbw cmd;
|
|
int timeout = USB_TIMEOUT_LONG;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageRequestSense\n"));
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct UstorageCbw));
|
|
cmd.signature = CBW_SIGNATURE;
|
|
cmd.tag = CBW_TAG_VALUE;
|
|
cmd.XferLen = 18;
|
|
cmd.dflags = CBWFLAGS_DIR_IN;
|
|
cmd.lun = 0;
|
|
cmd.CbLen = 6;
|
|
cmd.cb[0] = SCSI_REQUEST_SENSE;
|
|
cmd.cb[4] = 18;
|
|
|
|
return UsbBulkOnlyXfer(intf, &cmd, buffer, timeout);
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageTestUnitReady(struct uhintf* intf)
|
|
{
|
|
struct UstorageCbw cmd;
|
|
int timeout = USB_TIMEOUT_LONG;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageTestUnitReady\n"));
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct UstorageCbw));
|
|
cmd.signature = CBW_SIGNATURE;
|
|
cmd.tag = CBW_TAG_VALUE;
|
|
cmd.XferLen = 0;
|
|
cmd.dflags = CBWFLAGS_DIR_OUT;
|
|
cmd.lun = 0;
|
|
cmd.CbLen = 12;
|
|
cmd.cb[0] = SCSI_TEST_UNIT_READY;
|
|
|
|
return UsbBulkOnlyXfer(intf, &cmd, NONE, timeout);
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageInquiry(struct uhintf* intf, uint8* buffer)
|
|
{
|
|
struct UstorageCbw cmd;
|
|
int timeout = USB_TIMEOUT_LONG;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageInquiry\n"));
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct UstorageCbw));
|
|
cmd.signature = CBW_SIGNATURE;
|
|
cmd.tag = CBW_TAG_VALUE;
|
|
cmd.XferLen = 36;
|
|
cmd.dflags = CBWFLAGS_DIR_IN;
|
|
cmd.lun = 0;
|
|
cmd.CbLen = 6;
|
|
cmd.cb[0] = SCSI_INQUIRY_CMD;
|
|
cmd.cb[4] = 36;
|
|
|
|
return UsbBulkOnlyXfer(intf, &cmd, buffer, timeout);
|
|
}
|
|
|
|
|
|
x_err_t UsbhStorageGetCapacity(struct uhintf* intf, uint8* buffer)
|
|
{
|
|
struct UstorageCbw cmd;
|
|
int timeout = USB_TIMEOUT_LONG;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
NULL_PARAM_CHECK(intf->device);
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("UsbhStorageGetCapacity\n"));
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct UstorageCbw));
|
|
cmd.signature = CBW_SIGNATURE;
|
|
cmd.tag = CBW_TAG_VALUE;
|
|
cmd.XferLen = 8;
|
|
cmd.dflags = CBWFLAGS_DIR_IN;
|
|
cmd.lun = 0;
|
|
cmd.CbLen = 12;
|
|
cmd.cb[0] = SCSI_READ_CAPACITY;
|
|
|
|
return UsbBulkOnlyXfer(intf, &cmd, buffer, timeout);
|
|
}
|
|
|
|
|
|
static x_err_t UsbhStorageEnable(void* arg)
|
|
{
|
|
int i = 0;
|
|
x_err_t ret;
|
|
UstorPointer stor;
|
|
struct uhintf* intf = (struct uhintf*)arg;
|
|
|
|
|
|
if(intf == NONE)
|
|
{
|
|
KPrintf("the interface is not available\n");
|
|
return -EPIO;
|
|
}
|
|
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("subclass %d, protocal %d\n",
|
|
intf->IntfDesc->bInterfaceSubClass,
|
|
intf->IntfDesc->bInterfaceProtocol));
|
|
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("usbh_storage_run\n"));
|
|
|
|
|
|
|
|
stor = x_malloc(sizeof(struct ustor));
|
|
NULL_PARAM_CHECK(stor);
|
|
|
|
|
|
memset(stor, 0, sizeof(struct ustor));
|
|
intf->UserData = (void*)stor;
|
|
|
|
for(i=0; i<intf->IntfDesc->bNumEndpoints; i++)
|
|
{
|
|
UepDescPointer EpDesc;
|
|
|
|
|
|
UsbhGetEndpointDescriptor(intf->IntfDesc, i, &EpDesc);
|
|
if(EpDesc == NONE)
|
|
{
|
|
KPrintf("usb_get_endpoint_descriptor error\n");
|
|
return -ERROR;
|
|
}
|
|
|
|
|
|
if((EpDesc->bmAttributes & USB_EP_ATTR_TYPE_MASK) != USB_EP_ATTR_BULK)
|
|
continue;
|
|
|
|
|
|
if(EpDesc->bEndpointAddress & USB_DIR_IN)
|
|
{
|
|
|
|
stor->pipe_in = UsbInstanceFindPipe(intf->device,EpDesc->bEndpointAddress);
|
|
}
|
|
else
|
|
{
|
|
|
|
stor->pipe_out = UsbInstanceFindPipe(intf->device,EpDesc->bEndpointAddress);
|
|
}
|
|
}
|
|
|
|
|
|
if(stor->pipe_in == NONE || stor->pipe_out == NONE)
|
|
{
|
|
KPrintf("pipe error, unsupported device\n");
|
|
return -ERROR;
|
|
}
|
|
|
|
|
|
ret = UdiskRun(intf);
|
|
if(ret != EOK) return ret;
|
|
|
|
return EOK;
|
|
}
|
|
|
|
|
|
static x_err_t UsbhStorageDisable(void* arg)
|
|
{
|
|
UstorPointer stor;
|
|
struct uhintf* intf = (struct uhintf*)arg;
|
|
|
|
|
|
NULL_PARAM_CHECK(intf);
|
|
NULL_PARAM_CHECK(intf->UserData);
|
|
NULL_PARAM_CHECK(intf->device );
|
|
|
|
SYS_KDEBUG_LOG(SYS_DEBUG_USB, ("usbh_storage_stop\n"));
|
|
|
|
|
|
stor = (UstorPointer)intf->UserData;
|
|
|
|
UdiskStop(intf);
|
|
|
|
|
|
|
|
if(stor != NONE) x_free(stor);
|
|
return EOK;
|
|
}
|
|
|
|
|
|
UcdPointer UsbhClassDriverStorage(void)
|
|
{
|
|
StorageDriver.ClassCode = USB_CLASS_MASS_STORAGE;
|
|
|
|
StorageDriver.enable = UsbhStorageEnable;
|
|
StorageDriver.disable = UsbhStorageDisable;
|
|
|
|
return &StorageDriver;
|
|
}
|
|
|
|
#endif |