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:
Frank Wang 2017-10-25 17:19:44 +08:00 committed by Kever Yang
parent b3032eb3f0
commit f16e43f88d
4 changed files with 435 additions and 2 deletions

View File

@ -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;

View File

@ -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);

72
include/rockusb.h Normal file
View File

@ -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__ */

View File

@ -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;