diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 1ecb92ac6b..ce8a70bcb3 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -250,6 +250,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/drivers/usb/gadget/f_rockusb.c b/drivers/usb/gadget/f_rockusb.c new file mode 100644 index 0000000000..b5d350a975 --- /dev/null +++ b/drivers/usb/gadget/f_rockusb.c @@ -0,0 +1,332 @@ +/* + * Copyright 2017 Rockchip Electronics Co., Ltd + * Frank Wang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#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); diff --git a/include/rockusb.h b/include/rockusb.h new file mode 100644 index 0000000000..6e8647b7dd --- /dev/null +++ b/include/rockusb.h @@ -0,0 +1,72 @@ +/* + * Copyright 2017 Rockchip Electronics Co., Ltd + * Frank Wang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ROCKUSB_H__ +#define __ROCKUSB_H__ + +#include +#include +#include + +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__ */ diff --git a/include/usb_mass_storage.h b/include/usb_mass_storage.h index 8229f62b97..dc2b85e029 100644 --- a/include/usb_mass_storage.h +++ b/include/usb_mass_storage.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;