aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/sandbox_scsi.c132
-rw-r--r--drivers/scsi/scsi.c6
-rw-r--r--drivers/scsi/scsi_emul.c74
4 files changed, 207 insertions, 6 deletions
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 25194eeec1..d1b40c6140 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -17,4 +17,5 @@ endif
ifdef CONFIG_SCSI
obj-$(CONFIG_SANDBOX) += sandbox_scsi.o
+obj-$(CONFIG_SANDBOX) += scsi_emul.o
endif
diff --git a/drivers/scsi/sandbox_scsi.c b/drivers/scsi/sandbox_scsi.c
index 39b969a4b2..a7ac33cb1c 100644
--- a/drivers/scsi/sandbox_scsi.c
+++ b/drivers/scsi/sandbox_scsi.c
@@ -7,19 +7,145 @@
* that CONFIG_SCSI can be enabled for sandbox.
*/
+#define LOG_CATEGORY UCLASS_SCSI
+
#include <common.h>
+#include <dm.h>
+#include <os.h>
+#include <malloc.h>
#include <scsi.h>
+#include <scsi_emul.h>
+
+enum {
+ SANDBOX_SCSI_BLOCK_LEN = 512,
+ SANDBOX_SCSI_BUF_SIZE = 512,
+};
+
+/**
+ * struct sandbox_scsi_priv
+ *
+ * @eminfo: emulator state
+ * @pathanme: Path to the backing file, e.g. 'scsi.img'
+ * @fd: File descriptor of backing file
+ */
+struct sandbox_scsi_priv {
+ struct scsi_emul_info eminfo;
+ const char *pathname;
+ int fd;
+};
-int scsi_bus_reset(struct udevice *dev)
+static int sandbox_scsi_exec(struct udevice *dev, struct scsi_cmd *req)
{
+ struct sandbox_scsi_priv *priv = dev_get_priv(dev);
+ struct scsi_emul_info *info = &priv->eminfo;
+ int ret;
+
+ if (req->lun || req->target)
+ return -EIO;
+ ret = sb_scsi_emul_command(info, req, req->cmdlen);
+ if (ret < 0) {
+ log_debug("SCSI command 0x%02x ret errno %d\n", req->cmd[0],
+ ret);
+ return ret;
+ } else if (ret == SCSI_EMUL_DO_READ && priv->fd != -1) {
+ long bytes_read;
+
+ log_debug("read %x %x\n", info->seek_block, info->read_len);
+ os_lseek(priv->fd, info->seek_block * info->block_size,
+ OS_SEEK_SET);
+ bytes_read = os_read(priv->fd, req->pdata, info->buff_used);
+ if (bytes_read < 0)
+ return bytes_read;
+ if (bytes_read != info->buff_used)
+ return -EIO;
+ } else if (!ret) {
+ req->pdata = info->buff;
+ info->phase = SCSIPH_STATUS;
+ log_debug("sending buf\n");
+ } else {
+ log_debug("error\n");
+ return -EIO;
+ }
+
return 0;
}
-void scsi_init(void)
+static int sandbox_scsi_bus_reset(struct udevice *dev)
{
+ /* Not implemented */
+
+ return 0;
}
-int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
+static int sandbox_scsi_of_to_plat(struct udevice *dev)
{
+ struct sandbox_scsi_priv *priv = dev_get_priv(dev);
+
+ priv->pathname = dev_read_string(dev, "sandbox,filepath");
+
return 0;
}
+
+static int sandbox_scsi_probe(struct udevice *dev)
+{
+ struct scsi_plat *scsi_plat = dev_get_uclass_plat(dev);
+ struct sandbox_scsi_priv *priv = dev_get_priv(dev);
+ struct scsi_emul_info *info = &priv->eminfo;
+ int ret;
+
+ scsi_plat->max_id = 2;
+ scsi_plat->max_lun = 3;
+ scsi_plat->max_bytes_per_req = 1 << 20;
+
+ info->vendor = "SANDBOX";
+ info->product = "FAKE DISK";
+ info->buff = malloc(SANDBOX_SCSI_BUF_SIZE);
+ if (!info->buff)
+ return log_ret(-ENOMEM);
+ info->block_size = SANDBOX_SCSI_BLOCK_LEN;
+
+ if (priv->pathname) {
+ priv->fd = os_open(priv->pathname, OS_O_RDONLY);
+ if (priv->fd != -1) {
+ ret = os_get_filesize(priv->pathname, &info->file_size);
+ if (ret)
+ return log_msg_ret("sz", ret);
+ }
+ } else {
+ priv->fd = -1;
+ }
+ log_debug("filename: %s, fd %d\n", priv->pathname, priv->fd);
+
+ return 0;
+}
+
+static int sandbox_scsi_remove(struct udevice *dev)
+{
+ struct sandbox_scsi_priv *priv = dev_get_priv(dev);
+ struct scsi_emul_info *info = &priv->eminfo;
+
+ free(info->buff);
+
+ return 0;
+}
+
+struct scsi_ops sandbox_scsi_ops = {
+ .exec = sandbox_scsi_exec,
+ .bus_reset = sandbox_scsi_bus_reset,
+};
+
+static const struct udevice_id sanbox_scsi_ids[] = {
+ { .compatible = "sandbox,scsi" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_scsi) = {
+ .name = "sandbox_scsi",
+ .id = UCLASS_SCSI,
+ .ops = &sandbox_scsi_ops,
+ .of_match = sanbox_scsi_ids,
+ .of_to_plat = sandbox_scsi_of_to_plat,
+ .probe = sandbox_scsi_probe,
+ .remove = sandbox_scsi_remove,
+ .priv_auto = sizeof(struct sandbox_scsi_priv),
+};
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 99be5aef87..3e769b0843 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -456,7 +456,7 @@ static void scsi_init_dev_desc(struct blk_desc *dev_desc, int devnum)
{
dev_desc->lba = 0;
dev_desc->blksz = 0;
- dev_desc->if_type = UCLASS_SCSI;
+ dev_desc->uclass_id = UCLASS_SCSI;
dev_desc->devnum = devnum;
dev_desc->part_type = PART_TYPE_UNKNOWN;
@@ -706,8 +706,8 @@ U_BOOT_DRIVER(scsi_blk) = {
};
#else
U_BOOT_LEGACY_BLK(scsi) = {
- .if_typename = "scsi",
- .if_type = UCLASS_SCSI,
+ .uclass_idname = "scsi",
+ .uclass_id = UCLASS_SCSI,
.max_devs = SCSI_MAX_DEVICE,
.desc = scsi_dev_desc,
};
diff --git a/drivers/scsi/scsi_emul.c b/drivers/scsi/scsi_emul.c
new file mode 100644
index 0000000000..5ba364bdac
--- /dev/null
+++ b/drivers/scsi/scsi_emul.c
@@ -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;
+}