A well known unit can read and write fine while refusing READ CAPACITY, because it presents another unit's medium rather than one of its own. The scan wrote such a unit off, since it has no geometry to build a block device from.
Ask the controller which unit it currently presents and take the geometry from there, leaving the block device addressed by the well known unit so that the device keeps resolving where a request lands. Note that in cases where this matters (e.g. UFS boot W-LU) the physical underlying units should anyway have identical geometry to be usable in the UFS A/B boot scheme, so switching the active unit should not affect the geometry for real-world use. Signed-off-by: Alexey Charkov <[email protected]> --- drivers/scsi/scsi-uclass.c | 10 ++++++++++ drivers/scsi/scsi.c | 31 +++++++++++++++++++++++++------ include/scsi.h | 25 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/scsi-uclass.c b/drivers/scsi/scsi-uclass.c index 54afac6eaf51..ca6a16cd8206 100644 --- a/drivers/scsi/scsi-uclass.c +++ b/drivers/scsi/scsi-uclass.c @@ -50,6 +50,16 @@ int scsi_get_blk_by_uuid(const char *uuid, return -ENODEV; } +int scsi_wlun_alias(struct udevice *dev, int lun) +{ + struct scsi_ops *ops = scsi_get_ops(dev); + + if (!ops->wlun_alias) + return -ENOSYS; + + return ops->wlun_alias(dev, lun); +} + int scsi_bus_reset(struct udevice *dev) { struct scsi_ops *ops = scsi_get_ops(dev); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index aa7f9f1004d0..a60843e505ea 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -504,6 +504,7 @@ static bool scsi_unit_attention(struct scsi_cmd *pccb) * * @target: target id * @lun: target lun + * @wlun: true if this is a well known unit * @dev_desc: block device description * * The scsi_detect_dev detects and fills a dev_desc structure when the device is @@ -511,7 +512,7 @@ static bool scsi_unit_attention(struct scsi_cmd *pccb) * * Return: 0 on success, error value otherwise */ -static int scsi_detect_dev(struct udevice *dev, int target, int lun, +static int scsi_detect_dev(struct udevice *dev, int target, int lun, bool wlun, struct blk_desc *dev_desc) { unsigned char perq, modi; @@ -584,8 +585,25 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, return -EINVAL; } if (scsi_read_capacity(dev, pccb, &capacity, &blksz)) { - scsi_print_error(pccb); - return -EINVAL; + int alias = wlun ? scsi_wlun_alias(dev, lun) : -ENOENT; + + /* + * A well known unit reads and writes fine while declining to + * describe a medium of its own, so take the geometry from the + * unit whose medium it presents. + */ + if (alias < 0) { + scsi_print_error(pccb); + return -EINVAL; + } + + pccb->lun = alias; + err = scsi_read_capacity(dev, pccb, &capacity, &blksz); + pccb->lun = lun; + if (err) { + scsi_print_error(pccb); + return -EINVAL; + } } dev_desc->lba = capacity; dev_desc->blksz = blksz; @@ -599,7 +617,8 @@ removable: * (re)-scan the scsi bus and reports scsi device info * to the user if mode = 1 */ -static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) +static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool wlun, + bool verbose) { int ret; struct udevice *bdev; @@ -612,7 +631,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) * size, number of blocks) and other parameters (ids, type, ...) */ scsi_init_dev_desc_priv(&bd); - if (scsi_detect_dev(dev, id, lun, &bd)) + if (scsi_detect_dev(dev, id, lun, wlun, &bd)) return -ENODEV; /* @@ -677,7 +696,7 @@ int scsi_scan_dev(struct udevice *dev, bool verbose) for (i = 0; i < uc_plat->max_id; i++) for (lun = 0; lun < uc_plat->max_lun; lun++) - do_scsi_scan_one(dev, i, lun, verbose); + do_scsi_scan_one(dev, i, lun, false, verbose); return 0; } diff --git a/include/scsi.h b/include/scsi.h index 2520a8b8fe63..72fb6e4bd90d 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -296,6 +296,21 @@ struct scsi_ops { */ int (*bus_reset)(struct udevice *dev); + /** + * wlun_alias() - find the unit a well known unit stands in for + * + * A well known unit may read and write fine while declining to + * describe a medium of its own. This optional callback reports the + * ordinary unit whose medium it currently presents, so that a caller + * can take the geometry from there. Which unit that is may change over + * the life of the device, so it is resolved on each scan. + * + * @dev: SCSI bus + * @lun: Well known unit to resolve + * @return LUN it stands in for, -ve if it stands in for nothing + */ + int (*wlun_alias)(struct udevice *dev, int lun); + #if IS_ENABLED(CONFIG_BOUNCE_BUFFER) /** * buffer_aligned() - test memory alignment of block operation buffer @@ -336,6 +351,16 @@ int scsi_exec(struct udevice *dev, struct scsi_cmd *cmd); */ int scsi_bus_reset(struct udevice *dev); +/** + * scsi_wlun_alias() - Find the unit a well known unit stands in for + * + * @dev: SCSI bus + * @lun: Well known unit to resolve + * Return: LUN it stands in for, -ve if it stands in for nothing or the + * controller cannot tell + */ +int scsi_wlun_alias(struct udevice *dev, int lun); + /** * scsi_scan() - Scan all SCSI controllers for available devices * -- 2.54.0
