sandbox: scsi: Move request-handling code to scsi_emul
Move this code into the emulator file so it can be used by multiple drivers. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
@@ -17,4 +17,5 @@ endif
|
|||||||
|
|
||||||
ifdef CONFIG_SCSI
|
ifdef CONFIG_SCSI
|
||||||
obj-$(CONFIG_SANDBOX) += sandbox_scsi.o
|
obj-$(CONFIG_SANDBOX) += sandbox_scsi.o
|
||||||
|
obj-$(CONFIG_SANDBOX) += scsi_emul.o
|
||||||
endif
|
endif
|
||||||
|
74
drivers/scsi/scsi_emul.c
Normal file
74
drivers/scsi/scsi_emul.c
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Emulation of enough SCSI commands to find and read from a unit
|
||||||
|
*
|
||||||
|
* Copyright 2022 Google LLC
|
||||||
|
* Written by Simon Glass <sjg@chromium.org>
|
||||||
|
*
|
||||||
|
* implementation of SCSI functions required so that CONFIG_SCSI can be enabled
|
||||||
|
* for sandbox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_CATEGORY UCLASS_SCSI
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <dm.h>
|
||||||
|
#include <log.h>
|
||||||
|
#include <scsi.h>
|
||||||
|
#include <scsi_emul.h>
|
||||||
|
|
||||||
|
int sb_scsi_emul_command(struct scsi_emul_info *info,
|
||||||
|
const struct scsi_cmd *req, int len)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
info->buff_used = 0;
|
||||||
|
log_debug("emul %x\n", *req->cmd);
|
||||||
|
switch (*req->cmd) {
|
||||||
|
case SCSI_INQUIRY: {
|
||||||
|
struct scsi_inquiry_resp *resp = (void *)info->buff;
|
||||||
|
|
||||||
|
info->alloc_len = req->cmd[4];
|
||||||
|
memset(resp, '\0', sizeof(*resp));
|
||||||
|
resp->data_format = 1;
|
||||||
|
resp->additional_len = 0x1f;
|
||||||
|
strncpy(resp->vendor, info->vendor, sizeof(resp->vendor));
|
||||||
|
strncpy(resp->product, info->product, sizeof(resp->product));
|
||||||
|
strncpy(resp->revision, "1.0", sizeof(resp->revision));
|
||||||
|
info->buff_used = sizeof(*resp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCSI_TST_U_RDY:
|
||||||
|
break;
|
||||||
|
case SCSI_RD_CAPAC: {
|
||||||
|
struct scsi_read_capacity_resp *resp = (void *)info->buff;
|
||||||
|
uint blocks;
|
||||||
|
|
||||||
|
if (info->file_size)
|
||||||
|
blocks = info->file_size / info->block_size - 1;
|
||||||
|
else
|
||||||
|
blocks = 0;
|
||||||
|
resp->last_block_addr = cpu_to_be32(blocks);
|
||||||
|
resp->block_len = cpu_to_be32(info->block_size);
|
||||||
|
info->buff_used = sizeof(*resp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCSI_READ10: {
|
||||||
|
const struct scsi_read10_req *read_req = (void *)req;
|
||||||
|
|
||||||
|
info->seek_block = be32_to_cpu(read_req->lba);
|
||||||
|
info->read_len = be16_to_cpu(read_req->xfer_len);
|
||||||
|
info->buff_used = info->read_len * info->block_size;
|
||||||
|
ret = SCSI_EMUL_DO_READ;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
debug("Command not supported: %x\n", req->cmd[0]);
|
||||||
|
ret = -EPROTONOSUPPORT;
|
||||||
|
}
|
||||||
|
if (ret >= 0)
|
||||||
|
info->phase = info->transfer_len ? SCSIPH_DATA : SCSIPH_STATUS;
|
||||||
|
log_debug(" - done %x: ret=%d\n", *req->cmd, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
@@ -180,97 +180,30 @@ static void setup_response(struct sandbox_flash_priv *priv)
|
|||||||
csw->bCSWStatus = CSWSTATUS_GOOD;
|
csw->bCSWStatus = CSWSTATUS_GOOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int handle_ufi_command(struct sandbox_flash_priv *priv, const void *buff,
|
||||||
* handle_read() - prepare for reading data from the backing file
|
|
||||||
*
|
|
||||||
* This seeks to the correct file position and sets info->buff_used to the
|
|
||||||
* correct size.
|
|
||||||
*
|
|
||||||
* @priv: Private information
|
|
||||||
* @lba: Start block to read from
|
|
||||||
* @transfer_length: Number of blocks to read
|
|
||||||
* @return 0 if OK, -EIO on failure
|
|
||||||
*/
|
|
||||||
static int handle_read(struct sandbox_flash_priv *priv, ulong lba,
|
|
||||||
ulong transfer_len)
|
|
||||||
{
|
|
||||||
struct scsi_emul_info *info = &priv->eminfo;
|
|
||||||
|
|
||||||
debug("%s: lba=%lx, transfer_len=%lx\n", __func__, lba, transfer_len);
|
|
||||||
info->read_len = transfer_len;
|
|
||||||
if (priv->fd != -1) {
|
|
||||||
os_lseek(priv->fd, lba * info->block_size, OS_SEEK_SET);
|
|
||||||
info->buff_used = transfer_len * info->block_size;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_ufi_command(struct sandbox_flash_plat *plat,
|
|
||||||
struct sandbox_flash_priv *priv, const void *buff,
|
|
||||||
int len)
|
int len)
|
||||||
{
|
{
|
||||||
struct scsi_emul_info *info = &priv->eminfo;
|
struct scsi_emul_info *info = &priv->eminfo;
|
||||||
const struct scsi_cmd *req = buff;
|
const struct scsi_cmd *req = buff;
|
||||||
|
int ret;
|
||||||
|
|
||||||
info->buff_used = 0;
|
ret = sb_scsi_emul_command(info, req, len);
|
||||||
switch (*req->cmd) {
|
if (!ret) {
|
||||||
case SCSI_INQUIRY: {
|
|
||||||
struct scsi_inquiry_resp *resp = (void *)info->buff;
|
|
||||||
|
|
||||||
info->alloc_len = req->cmd[4];
|
|
||||||
memset(resp, '\0', sizeof(*resp));
|
|
||||||
resp->data_format = 1;
|
|
||||||
resp->additional_len = 0x1f;
|
|
||||||
strncpy(resp->vendor, info->vendor, sizeof(resp->vendor));
|
|
||||||
strncpy(resp->product, info->product, sizeof(resp->product));
|
|
||||||
strncpy(resp->revision, "1.0", sizeof(resp->revision));
|
|
||||||
info->buff_used = sizeof(*resp);
|
|
||||||
setup_response(priv);
|
setup_response(priv);
|
||||||
break;
|
} else if (ret == SCSI_EMUL_DO_READ && priv->fd != -1) {
|
||||||
}
|
os_lseek(priv->fd, info->seek_block * info->block_size,
|
||||||
case SCSI_TST_U_RDY:
|
OS_SEEK_SET);
|
||||||
setup_response(priv);
|
setup_response(priv);
|
||||||
break;
|
} else {
|
||||||
case SCSI_RD_CAPAC: {
|
setup_fail_response(priv);
|
||||||
struct scsi_read_capacity_resp *resp = (void *)info->buff;
|
|
||||||
uint blocks;
|
|
||||||
|
|
||||||
if (info->file_size)
|
|
||||||
blocks = info->file_size / info->block_size - 1;
|
|
||||||
else
|
|
||||||
blocks = 0;
|
|
||||||
resp->last_block_addr = cpu_to_be32(blocks);
|
|
||||||
resp->block_len = cpu_to_be32(info->block_size);
|
|
||||||
info->buff_used = sizeof(*resp);
|
|
||||||
setup_response(priv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SCSI_READ10: {
|
|
||||||
struct scsi_read10_req *req = (void *)buff;
|
|
||||||
|
|
||||||
if (!handle_read(priv, be32_to_cpu(req->lba),
|
|
||||||
be16_to_cpu(req->xfer_len)))
|
|
||||||
setup_response(priv);
|
|
||||||
else
|
|
||||||
setup_fail_response(priv);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
debug("Command not supported: %x\n", req->cmd[0]);
|
|
||||||
return -EPROTONOSUPPORT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info->phase = info->transfer_len ? SCSIPH_DATA : SCSIPH_STATUS;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev,
|
static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev,
|
||||||
unsigned long pipe, void *buff, int len)
|
unsigned long pipe, void *buff, int len)
|
||||||
{
|
{
|
||||||
struct sandbox_flash_plat *plat = dev_get_plat(dev);
|
|
||||||
struct sandbox_flash_priv *priv = dev_get_priv(dev);
|
struct sandbox_flash_priv *priv = dev_get_priv(dev);
|
||||||
struct scsi_emul_info *info = &priv->eminfo;
|
struct scsi_emul_info *info = &priv->eminfo;
|
||||||
int ep = usb_pipeendpoint(pipe);
|
int ep = usb_pipeendpoint(pipe);
|
||||||
@@ -294,7 +227,7 @@ static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev,
|
|||||||
goto err;
|
goto err;
|
||||||
info->transfer_len = cbw->dCBWDataTransferLength;
|
info->transfer_len = cbw->dCBWDataTransferLength;
|
||||||
priv->tag = cbw->dCBWTag;
|
priv->tag = cbw->dCBWTag;
|
||||||
return handle_ufi_command(plat, priv, cbw->CBWCDB,
|
return handle_ufi_command(priv, cbw->CBWCDB,
|
||||||
cbw->bCDBLength);
|
cbw->bCDBLength);
|
||||||
case SCSIPH_DATA:
|
case SCSIPH_DATA:
|
||||||
debug("data out\n");
|
debug("data out\n");
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
* @product: Product name
|
* @product: Product name
|
||||||
* @block_size: Block size of device in bytes (normally 512)
|
* @block_size: Block size of device in bytes (normally 512)
|
||||||
* @file_size: Size of the backing file for this emulator, in bytes
|
* @file_size: Size of the backing file for this emulator, in bytes
|
||||||
|
* @seek_block: Seek position for file (block number)
|
||||||
*
|
*
|
||||||
* @phase: Current SCSI phase
|
* @phase: Current SCSI phase
|
||||||
* @buff_used: Number of bytes ready to transfer back to host
|
* @buff_used: Number of bytes ready to transfer back to host
|
||||||
@@ -34,13 +35,36 @@ struct scsi_emul_info {
|
|||||||
const char *product;
|
const char *product;
|
||||||
int block_size;
|
int block_size;
|
||||||
loff_t file_size;
|
loff_t file_size;
|
||||||
|
int seek_block;
|
||||||
|
|
||||||
/* state maintained by the emulator: */
|
/* state maintained by the emulator: */
|
||||||
enum scsi_cmd_phase phase;
|
enum scsi_cmd_phase phase;
|
||||||
int buff_used;
|
int buff_used;
|
||||||
int read_len;
|
int read_len;
|
||||||
|
uint seek_pos;
|
||||||
int alloc_len;
|
int alloc_len;
|
||||||
uint transfer_len;
|
uint transfer_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Indicates that a read is being started */
|
||||||
|
#define SCSI_EMUL_DO_READ 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sb_scsi_emul_command() - Process a SCSI command
|
||||||
|
*
|
||||||
|
* This sets up the response in info->buff and updates various other values
|
||||||
|
* in info.
|
||||||
|
*
|
||||||
|
* If SCSI_EMUL_DO_READ is returned then the caller should set up so that the
|
||||||
|
* backing file can be read, or return an error status if there is no file.
|
||||||
|
*
|
||||||
|
* @info: Emulation information
|
||||||
|
* @req: Request to process
|
||||||
|
* @len: Length of request in bytes
|
||||||
|
* @return SCSI_EMUL_DO_READ if a read has started, 0 if some other operation
|
||||||
|
* has started, -ve if there was an error
|
||||||
|
*/
|
||||||
|
int sb_scsi_emul_command(struct scsi_emul_info *info,
|
||||||
|
const struct scsi_cmd *req, int len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user