--- /dev/null
+// 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;
+}
csw->bCSWStatus = CSWSTATUS_GOOD;
}
-/**
- * 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,
+static int handle_ufi_command(struct sandbox_flash_priv *priv, const void *buff,
int len)
{
struct scsi_emul_info *info = &priv->eminfo;
const struct scsi_cmd *req = buff;
+ int ret;
- info->buff_used = 0;
- 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);
- setup_response(priv);
- break;
- }
- case SCSI_TST_U_RDY:
+ ret = sb_scsi_emul_command(info, req, len);
+ if (!ret) {
setup_response(priv);
- 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);
+ } else if (ret == SCSI_EMUL_DO_READ && priv->fd != -1) {
+ os_lseek(priv->fd, info->seek_block * info->block_size,
+ OS_SEEK_SET);
setup_response(priv);
- break;
+ } else {
+ setup_fail_response(priv);
}
- 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;
}
static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev,
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 scsi_emul_info *info = &priv->eminfo;
int ep = usb_pipeendpoint(pipe);
goto err;
info->transfer_len = cbw->dCBWDataTransferLength;
priv->tag = cbw->dCBWTag;
- return handle_ufi_command(plat, priv, cbw->CBWCDB,
+ return handle_ufi_command(priv, cbw->CBWCDB,
cbw->bCDBLength);
case SCSIPH_DATA:
debug("data out\n");
* @product: Product name
* @block_size: Block size of device in bytes (normally 512)
* @file_size: Size of the backing file for this emulator, in bytes
+ * @seek_block: Seek position for file (block number)
*
* @phase: Current SCSI phase
* @buff_used: Number of bytes ready to transfer back to host
const char *product;
int block_size;
loff_t file_size;
+ int seek_block;
/* state maintained by the emulator: */
enum scsi_cmd_phase phase;
int buff_used;
int read_len;
+ uint seek_pos;
int alloc_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