usb: gadget: add a new rockusb gadget driver
This patch add a new rockusb gadget driver function. For the design, simply embeding rockusb code into USB Mass Storage function as using UMS framework can prominently improve upgrade speed and devices stability. This change does not affect the original USB Mass Storage function, the rockusb process are just checked by IS_RKUSB_UMS_DNL macro which only run when CONFIG_CMD_ROCKUSB was defined and rkusb_ums_dnl composite driver was registed. Change-Id: I9c2723200c7e02d947587037abcd7d3badf4256c Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
This commit is contained in:
parent
b3032eb3f0
commit
f16e43f88d
|
|
@ -250,6 +250,7 @@
|
|||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <usb_mass_storage.h>
|
||||
#include <rockusb.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
|
@ -1670,6 +1671,9 @@ static int send_status(struct fsg_common *common)
|
|||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
#ifdef CONFIG_CMD_ROCKUSB
|
||||
#include "f_rockusb.c"
|
||||
#endif
|
||||
|
||||
/* Check whether the command is properly formed and whether its data size
|
||||
* and direction agree with the values we already have. */
|
||||
|
|
@ -1795,6 +1799,7 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
int i;
|
||||
static char unknown[16];
|
||||
struct fsg_lun *curlun = &common->luns[common->lun];
|
||||
const char *cdev_name __maybe_unused;
|
||||
|
||||
dump_cdb(common);
|
||||
|
||||
|
|
@ -1810,6 +1815,16 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
common->short_packet_received = 0;
|
||||
|
||||
down_read(&common->filesem); /* We're using the backing file */
|
||||
|
||||
cdev_name = common->fsg->function.config->cdev->driver->name;
|
||||
if (IS_RKUSB_UMS_DNL(cdev_name)) {
|
||||
rc = rkusb_cmd_process(common, bh, &reply);
|
||||
if (rc == RKUSB_RC_FINISHED || rc == RKUSB_RC_ERROR)
|
||||
goto finish;
|
||||
else if (rc == RKUSB_RC_UNKNOWN_CMND)
|
||||
goto unknown_cmnd;
|
||||
}
|
||||
|
||||
switch (common->cmnd[0]) {
|
||||
|
||||
case SC_INQUIRY:
|
||||
|
|
@ -2038,6 +2053,8 @@ unknown_cmnd:
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
finish:
|
||||
up_read(&common->filesem);
|
||||
|
||||
if (reply == -EINTR)
|
||||
|
|
@ -2693,7 +2710,10 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fsg->bulk_out = ep;
|
||||
|
||||
/* Copy descriptors */
|
||||
f->descriptors = usb_copy_descriptors(fsg_fs_function);
|
||||
if (IS_RKUSB_UMS_DNL(c->cdev->driver->name))
|
||||
f->descriptors = usb_copy_descriptors(rkusb_fs_function);
|
||||
else
|
||||
f->descriptors = usb_copy_descriptors(fsg_fs_function);
|
||||
if (unlikely(!f->descriptors))
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -2703,7 +2723,13 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fsg_fs_bulk_in_desc.bEndpointAddress;
|
||||
fsg_hs_bulk_out_desc.bEndpointAddress =
|
||||
fsg_fs_bulk_out_desc.bEndpointAddress;
|
||||
f->hs_descriptors = usb_copy_descriptors(fsg_hs_function);
|
||||
|
||||
if (IS_RKUSB_UMS_DNL(c->cdev->driver->name))
|
||||
f->hs_descriptors =
|
||||
usb_copy_descriptors(rkusb_hs_function);
|
||||
else
|
||||
f->hs_descriptors =
|
||||
usb_copy_descriptors(fsg_hs_function);
|
||||
if (unlikely(!f->hs_descriptors)) {
|
||||
free(f->descriptors);
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
* Frank Wang <frank.wang@rock-chips.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <rockusb.h>
|
||||
|
||||
#define ROCKUSB_INTERFACE_CLASS 0xff
|
||||
#define ROCKUSB_INTERFACE_SUB_CLASS 0x06
|
||||
#define ROCKUSB_INTERFACE_PROTOCOL 0x05
|
||||
|
||||
static struct usb_interface_descriptor rkusb_intf_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0x00,
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x02,
|
||||
.bInterfaceClass = ROCKUSB_INTERFACE_CLASS,
|
||||
.bInterfaceSubClass = ROCKUSB_INTERFACE_SUB_CLASS,
|
||||
.bInterfaceProtocol = ROCKUSB_INTERFACE_PROTOCOL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *rkusb_fs_function[] = {
|
||||
(struct usb_descriptor_header *)&rkusb_intf_desc,
|
||||
(struct usb_descriptor_header *)&fsg_fs_bulk_in_desc,
|
||||
(struct usb_descriptor_header *)&fsg_fs_bulk_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *rkusb_hs_function[] = {
|
||||
(struct usb_descriptor_header *)&rkusb_intf_desc,
|
||||
(struct usb_descriptor_header *)&fsg_hs_bulk_in_desc,
|
||||
(struct usb_descriptor_header *)&fsg_hs_bulk_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct rk_flash_info {
|
||||
u32 flash_size;
|
||||
u16 block_size;
|
||||
u8 page_size;
|
||||
u8 ecc_bits;
|
||||
u8 access_time;
|
||||
u8 manufacturer;
|
||||
u8 flash_mask;
|
||||
} __packed;
|
||||
|
||||
int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
|
||||
{
|
||||
/* Enumerate as a loader device */
|
||||
if (IS_RKUSB_UMS_DNL(name))
|
||||
dev->bcdUSB = cpu_to_le16(0x0201);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__maybe_unused
|
||||
static inline void dump_cbw(struct fsg_bulk_cb_wrap *cbw)
|
||||
{
|
||||
assert(!cbw);
|
||||
|
||||
debug("%s:\n", __func__);
|
||||
debug("Signature %x\n", cbw->Signature);
|
||||
debug("Tag %x\n", cbw->Tag);
|
||||
debug("DataTransferLength %x\n", cbw->DataTransferLength);
|
||||
debug("Flags %x\n", cbw->Flags);
|
||||
debug("LUN %x\n", cbw->Lun);
|
||||
debug("Length %x\n", cbw->Length);
|
||||
debug("OptionCode %x\n", cbw->CDB[0]);
|
||||
debug("SubCode %x\n", cbw->CDB[1]);
|
||||
debug("SectorAddr %x\n", get_unaligned_be32(&cbw->CDB[2]));
|
||||
debug("BlkSectors %x\n\n", get_unaligned_be16(&cbw->CDB[7]));
|
||||
}
|
||||
|
||||
static int rkusb_check_lun(struct fsg_common *common)
|
||||
{
|
||||
struct fsg_lun *curlun;
|
||||
|
||||
/* Check the LUN */
|
||||
if (common->lun >= 0 && common->lun < common->nluns) {
|
||||
curlun = &common->luns[common->lun];
|
||||
if (common->cmnd[0] != SC_REQUEST_SENSE) {
|
||||
curlun->sense_data = SS_NO_SENSE;
|
||||
curlun->info_valid = 0;
|
||||
}
|
||||
} else {
|
||||
curlun = NULL;
|
||||
common->bad_lun_okay = 0;
|
||||
|
||||
/*
|
||||
* INQUIRY and REQUEST SENSE commands are explicitly allowed
|
||||
* to use unsupported LUNs; all others may not.
|
||||
*/
|
||||
if (common->cmnd[0] != SC_INQUIRY &&
|
||||
common->cmnd[0] != SC_REQUEST_SENSE) {
|
||||
debug("unsupported LUN %d\n", common->lun);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __do_reset(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
do_reset(NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static int rkusb_do_reset(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
common->data_size_from_cmnd = common->cmnd[4];
|
||||
common->residue = 0;
|
||||
bh->inreq->complete = __do_reset;
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rkusb_do_test_unit_ready(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
common->residue = 0x06 << 24; /* Max block xfer support from host */
|
||||
common->data_dir = DATA_DIR_NONE;
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rkusb_do_read_flash_id(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
u8 *buf = (u8 *)bh->buf;
|
||||
u32 len = common->data_size;
|
||||
|
||||
memcpy((void *)&buf[0], "EMMC ", 5);
|
||||
|
||||
/* Set data xfer size */
|
||||
common->residue = common->data_size_from_cmnd = len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rkusb_do_test_bad_block(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
u8 *buf = (u8 *)bh->buf;
|
||||
u32 len = common->data_size;
|
||||
|
||||
memset((void *)&buf[0], 0, len);
|
||||
|
||||
/* Set data xfer size */
|
||||
common->residue = common->data_size_from_cmnd = len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rkusb_do_read_flash_info(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
u8 *buf = (u8 *)bh->buf;
|
||||
u32 len = common->data_size;
|
||||
struct rk_flash_info finfo = {
|
||||
.block_size = 1024,
|
||||
.ecc_bits = 0,
|
||||
.page_size = 4,
|
||||
.access_time = 40,
|
||||
.manufacturer = 0,
|
||||
.flash_mask = 0
|
||||
};
|
||||
|
||||
finfo.flash_size = (u32)ums[common->lun].block_dev.lba;
|
||||
if (finfo.flash_size)
|
||||
finfo.flash_mask = 1;
|
||||
|
||||
memset((void *)&buf[0], 0, len);
|
||||
memcpy((void *)&buf[0], (void *)&finfo, len);
|
||||
|
||||
/* Set data xfer size */
|
||||
common->residue = common->data_size_from_cmnd = len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rkusb_do_lba_erase(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
struct fsg_lun *curlun = &common->luns[common->lun];
|
||||
u32 lba, amount;
|
||||
loff_t file_offset;
|
||||
int rc;
|
||||
|
||||
lba = get_unaligned_be32(&common->cmnd[2]);
|
||||
if (lba >= curlun->num_sectors) {
|
||||
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
file_offset = ((loff_t) lba) << 9;
|
||||
amount = get_unaligned_be16(&common->cmnd[7]) << 9;
|
||||
if (unlikely(amount == 0)) {
|
||||
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Perform the erase */
|
||||
rc = ums[common->lun].erase_sector(&ums[common->lun],
|
||||
file_offset / SECTOR_SIZE,
|
||||
amount / SECTOR_SIZE);
|
||||
if (!rc) {
|
||||
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
|
||||
rc = -EIO;
|
||||
}
|
||||
|
||||
out:
|
||||
common->data_dir = DATA_DIR_NONE;
|
||||
bh->state = BUF_STATE_EMPTY;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rkusb_do_read_capacity(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh)
|
||||
{
|
||||
u8 *buf = (u8 *)bh->buf;
|
||||
u32 len = common->data_size;
|
||||
|
||||
/*
|
||||
* bit[0]: Direct LBA, 0: Disabled;
|
||||
* bit[1:63}: Reserved.
|
||||
*/
|
||||
memset((void *)&buf[0], 0, len);
|
||||
|
||||
/* Set data xfer size */
|
||||
common->residue = common->data_size_from_cmnd = len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rkusb_cmd_process(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh, int *reply)
|
||||
{
|
||||
struct usb_request *req = bh->outreq;
|
||||
struct fsg_bulk_cb_wrap *cbw = req->buf;
|
||||
int rc;
|
||||
|
||||
dump_cbw(cbw);
|
||||
|
||||
if (rkusb_check_lun(common)) {
|
||||
*reply = -EINVAL;
|
||||
return RKUSB_RC_ERROR;
|
||||
}
|
||||
|
||||
switch (common->cmnd[0]) {
|
||||
case RKUSB_TEST_UNIT_READY:
|
||||
*reply = rkusb_do_test_unit_ready(common, bh);
|
||||
rc = RKUSB_RC_FINISHED;
|
||||
break;
|
||||
|
||||
case RKUSB_READ_FLASH_ID:
|
||||
*reply = rkusb_do_read_flash_id(common, bh);
|
||||
rc = RKUSB_RC_FINISHED;
|
||||
break;
|
||||
|
||||
case RKUSB_TEST_BAD_BLOCK:
|
||||
*reply = rkusb_do_test_bad_block(common, bh);
|
||||
rc = RKUSB_RC_FINISHED;
|
||||
break;
|
||||
|
||||
case RKUSB_LBA_READ_10:
|
||||
common->cmnd[0] = SC_READ_10;
|
||||
common->cmnd[1] = 0; /* Not support */
|
||||
rc = RKUSB_RC_CONTINUE;
|
||||
break;
|
||||
|
||||
case RKUSB_LBA_WRITE_10:
|
||||
common->cmnd[0] = SC_WRITE_10;
|
||||
common->cmnd[1] = 0; /* Not support */
|
||||
rc = RKUSB_RC_CONTINUE;
|
||||
break;
|
||||
|
||||
case RKUSB_READ_FLASH_INFO:
|
||||
*reply = rkusb_do_read_flash_info(common, bh);
|
||||
rc = RKUSB_RC_FINISHED;
|
||||
break;
|
||||
|
||||
case RKUSB_LBA_ERASE:
|
||||
*reply = rkusb_do_lba_erase(common, bh);
|
||||
rc = RKUSB_RC_FINISHED;
|
||||
break;
|
||||
|
||||
case RKUSB_READ_CAPACITY:
|
||||
*reply = rkusb_do_read_capacity(common, bh);
|
||||
rc = RKUSB_RC_FINISHED;
|
||||
break;
|
||||
|
||||
case RKUSB_RESET:
|
||||
*reply = rkusb_do_reset(common, bh);
|
||||
rc = RKUSB_RC_FINISHED;
|
||||
break;
|
||||
|
||||
case RKUSB_SET_DEVICE_ID:
|
||||
case RKUSB_READ_10:
|
||||
case RKUSB_WRITE_10:
|
||||
case RKUSB_ERASE_10:
|
||||
case RKUSB_WRITE_SPARE:
|
||||
case RKUSB_READ_SPARE:
|
||||
case RKUSB_ERASE_10_FORCE:
|
||||
case RKUSB_GET_VERSION:
|
||||
case RKUSB_ERASE_SYS_DISK:
|
||||
case RKUSB_SDRAM_READ_10:
|
||||
case RKUSB_SDRAM_WRITE_10:
|
||||
case RKUSB_SDRAM_EXECUTE:
|
||||
case RKUSB_GET_CHIP_VER:
|
||||
case RKUSB_LOW_FORMAT:
|
||||
case RKUSB_SET_RESET_FLAG:
|
||||
case RKUSB_SPI_READ_10:
|
||||
case RKUSB_SPI_WRITE_10:
|
||||
case RKUSB_SESSION:
|
||||
/* Fall through */
|
||||
default:
|
||||
rc = RKUSB_RC_UNKNOWN_CMND;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
DECLARE_GADGET_BIND_CALLBACK(rkusb_ums_dnl, fsg_add);
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
* Frank Wang <frank.wang@rock-chips.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __ROCKUSB_H__
|
||||
#define __ROCKUSB_H__
|
||||
|
||||
#include <common.h>
|
||||
#include <part.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
enum rkusb_cmd {
|
||||
RKUSB_TEST_UNIT_READY = 0x00,
|
||||
RKUSB_READ_FLASH_ID = 0x01,
|
||||
RKUSB_SET_DEVICE_ID = 0x02,
|
||||
RKUSB_TEST_BAD_BLOCK = 0x03,
|
||||
RKUSB_READ_10 = 0x04,
|
||||
RKUSB_WRITE_10 = 0x05,
|
||||
RKUSB_ERASE_10 = 0x06,
|
||||
RKUSB_WRITE_SPARE = 0x07,
|
||||
RKUSB_READ_SPARE = 0x08,
|
||||
RKUSB_ERASE_10_FORCE = 0x0B,
|
||||
RKUSB_GET_VERSION = 0x0C,
|
||||
RKUSB_LBA_READ_10 = 0x14,
|
||||
RKUSB_LBA_WRITE_10 = 0x15,
|
||||
RKUSB_ERASE_SYS_DISK = 0x16,
|
||||
RKUSB_SDRAM_READ_10 = 0x17,
|
||||
RKUSB_SDRAM_WRITE_10 = 0x18,
|
||||
RKUSB_SDRAM_EXECUTE = 0x19,
|
||||
RKUSB_READ_FLASH_INFO = 0x1A,
|
||||
RKUSB_GET_CHIP_VER = 0x1B,
|
||||
RKUSB_LOW_FORMAT = 0x1C,
|
||||
RKUSB_SET_RESET_FLAG = 0x1E,
|
||||
RKUSB_SPI_READ_10 = 0x21,
|
||||
RKUSB_SPI_WRITE_10 = 0x22,
|
||||
RKUSB_LBA_ERASE = 0x25,
|
||||
RKUSB_SESSION = 0x30,
|
||||
RKUSB_READ_CAPACITY = 0xAA,
|
||||
RKUSB_RESET = 0xFF,
|
||||
};
|
||||
|
||||
enum rkusb_rc {
|
||||
RKUSB_RC_ERROR = -1,
|
||||
RKUSB_RC_CONTINUE = 0,
|
||||
RKUSB_RC_FINISHED = 1,
|
||||
RKUSB_RC_UNKNOWN_CMND = 2,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CMD_ROCKUSB
|
||||
#define IS_RKUSB_UMS_DNL(name) (!strncmp((name), "rkusb_ums_dnl", 13))
|
||||
#else
|
||||
#define IS_RKUSB_UMS_DNL(name) 0
|
||||
|
||||
struct fsg_buffhd;
|
||||
struct fsg_dev;
|
||||
struct fsg_common;
|
||||
struct fsg_config;
|
||||
|
||||
static struct usb_descriptor_header *rkusb_fs_function[];
|
||||
static struct usb_descriptor_header *rkusb_hs_function[];
|
||||
|
||||
static inline int rkusb_cmd_process(struct fsg_common *common,
|
||||
struct fsg_buffhd *bh, int *reply)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ROCKUSB_H__ */
|
||||
|
|
@ -20,6 +20,9 @@ struct ums {
|
|||
ulong start, lbaint_t blkcnt, void *buf);
|
||||
int (*write_sector)(struct ums *ums_dev,
|
||||
ulong start, lbaint_t blkcnt, const void *buf);
|
||||
#ifdef CONFIG_CMD_ROCKUSB
|
||||
int (*erase_sector)(struct ums *ums_dev, ulong start, lbaint_t blkcnt);
|
||||
#endif
|
||||
unsigned int start_sector;
|
||||
unsigned int num_sectors;
|
||||
const char *name;
|
||||
|
|
|
|||
Loading…
Reference in New Issue