diff mbox series

[6/6] FMS: Add SCSI Read Disc Information command.

Message ID 20210626211820.107310-7-i.kononenko@yadro.com
State New
Headers show
Series [1/6] usb:gadget:mass-storage: Improve the signature of SCSI handler function | expand

Commit Message

Igor Kononenko June 26, 2021, 9:18 p.m. UTC
Adds the SCSI Read Disc Information command, which should be given for
multimedia device consumers to retrieve the DVD/BD disk information
about:
* Total tracks contained at the disc Total and active sessions Border
* status(incomplete, damaged, etc.)
This information is wanted for supporting the DVD-ROM and BD-ROM
devices.

End-user-impact: Now, multimedia device consumers have a way to retrieve
                 the multimedia disk information.

Signed-off-by: Igor Kononenko <i.kononenko@yadro.com>
---
 drivers/usb/gadget/function/f_mass_storage.c |  37 ++++
 include/scsi/scsi_proto.h                    |   1 +
 include/uapi/linux/cdrom.h                   | 195 +++++++++++++++++--
 3 files changed, 212 insertions(+), 21 deletions(-)
diff mbox series

Patch

diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 7e736e5594f9..d3d8a806b5e6 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -1932,6 +1932,40 @@  static void send_status(struct fsg_common *common)
 	return;
 }
 
+static int do_read_disc_info(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun *curlun = common->curlun;
+	struct cdb_disc_info *cdb = (struct cdb_disc_info *)common->cmnd;
+	disc_information *info = (disc_information *)bh->buf;
+
+	if (cdb->type != DISC_TYPE_STANDARD) {
+		LERROR(curlun,
+		       "Unsupported disc information type(%02Xh) requested\n",
+		       cdb->type);
+		return -EINVAL;
+	}
+	memset(info, 0, sizeof(disc_information));
+	info->disc_information_length = cpu_to_be16(
+		sizeof(*info) - sizeof(info->disc_information_length));
+
+	info->border_status = DISC_LAST_SESS_COMPLETE;
+	info->disc_status = DISC_STATUS_FINALIZED;
+
+	/* We only support one session per disk */
+	info->n_first_track = 1;
+	info->n_sessions_lsb = 1;
+	info->first_track_lsb = 1;
+	info->last_track_lsb = 1;
+
+	/* Setting the unrestricted use because we only support (CD/DVD/BD)-ROM */
+	info->uru = 1;
+
+	info->disc_type = DISC_FIELD_DA_ROM;
+
+	common->data_size_to_handle = sizeof(*info);
+	return 0;
+}
+
 /**
  * Attempts to guess medium type by looking at the length of the disc layout.
  */
@@ -2253,6 +2287,8 @@  static struct cdb_command_check cdb_checker_table[] = {
 	{ CDB_REG_CHECKER(TEST_UNIT_READY, 6, CDB_NO_SIZE_FIELD, DATA_DIR_NONE,
 			  0x0000, MEDIUM_REQUIRED) },
 
+	{ CDB_REG_NO_CHECKER(READ_DISC_INFORMATION, CDB_SIZE_FIELD_7,
+			     DATA_DIR_TO_HOST, MEDIUM_REQUIRED) },
 	{ CDB_REG_NO_CHECKER(GET_CONFIGURATION, CDB_SIZE_FIELD_7,
 			     DATA_DIR_TO_HOST, MEDIUM_REQUIRED) },
 
@@ -2286,6 +2322,7 @@  static struct cdb_handler cdb_handlers_table[] = {
 	{ CDB_REG_HANDLER(SYNCHRONIZE_CACHE, &do_synchronize_cache) },
 	{ CDB_REG_HANDLER(TEST_UNIT_READY, NULL) },
 
+	{ CDB_REG_HANDLER_BUFFHD(READ_DISC_INFORMATION, &do_read_disc_info) },
 	{ CDB_REG_HANDLER_BUFFHD(GET_CONFIGURATION, &do_get_configuration) },
 	/*
 	 * Although optional, this command is used by MS-Windows.  We
diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h
index 6b2a8ee1f0a3..6728fcbd73e4 100644
--- a/include/scsi/scsi_proto.h
+++ b/include/scsi/scsi_proto.h
@@ -77,6 +77,7 @@ 
 #define GET_EVENT_STATUS_NOTIFICATION 0x4a
 #define LOG_SELECT            0x4c
 #define LOG_SENSE             0x4d
+#define READ_DISC_INFORMATION 0x51
 #define XDWRITEREAD_10        0x53
 #define MODE_SELECT_10        0x55
 #define RESERVE_10            0x56
diff --git a/include/uapi/linux/cdrom.h b/include/uapi/linux/cdrom.h
index 442693fdc059..460377e1a532 100644
--- a/include/uapi/linux/cdrom.h
+++ b/include/uapi/linux/cdrom.h
@@ -816,51 +816,204 @@  struct rwrt_feature_desc {
 	__u8 reserved3;
 };
 
+/* Disc Information Data Types */
+#define DISC_TYPE_STANDARD			(0x00U)
+#define DISC_TYPE_TRACK				(0x01U)
+#define DISC_TYPE_POW				(0x02U)
+
+/* Disc Status */
+#define DISC_STATUS_EMPTY			(0x00U)
+#define DISC_STATUS_INCOMPLETE		(0x01U)
+#define DISC_STATUS_FINALIZED		(0x02U)
+#define DISC_STATUS_OTHER			(0x03U)
+
+/* State of Last Session */
+#define DISC_LAST_SESS_EMPTY		(0x00U)
+#define DISC_LAST_SESS_INCOMPLETE	(0x01U)
+#define DISC_LAST_SESS_DAMAGED		(0x02U)
+#define DISC_LAST_SESS_COMPLETE		(0x03U)
+
+/* Background Format Status Codes */
+#define DISC_BACK_FMT_NEITHER		(0x00U)
+#define DISC_BACK_FMT_STARTED		(0x01U)
+#define DISC_BACK_FMT_PROGRESS		(0x02U)
+#define DISC_BACK_FMT_COMPLETED		(0x03U)
+
+/* Disc Type Field */
+#define DISC_FIELD_DA_ROM			(0x00U)
+#define DISC_FIELD_I				(0x10U)
+#define DISC_FIELD_ROM_XA			(0x20U)
+#define DISC_FIELD_UNDEF			(0xFFU)
+
+/**
+ * @brief The READ DISC INFORMATION CDB(0051h)
+ * The READ DISC INFORMATION command allows the Host to request information about
+ * the currently mounted MM disc.
+ */
+struct cdb_disc_info {
+	__u8 code;
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved1 : 5;
+	/**
+	 * When a disc is present, Data Type defines the specific information requested
+	 */
+	__u8 type : 3;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 type : 3;
+	__u8 reserved1 : 5;
+#endif
+
+	__u8 reserved2[5];
+
+	__be16 length;
+
+	__u8 control;
+}  __packed;
+
 typedef struct {
 	__be16 disc_information_length;
 #if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 reserved1			: 3;
-        __u8 erasable			: 1;
-        __u8 border_status		: 2;
-        __u8 disc_status		: 2;
+	/**
+	 * The Disc Information Data Type field shall be set to the reported
+	 * Disc Information Type
+	 */
+	__u8 info_data_type : 3;
+	/**
+	 * The Erasable bit, when set to one, indicates that CD-RW, DVD-RAM, DVD-RW, DVD+RW,
+	 * HD DVD-RAM, or BD-RE media is present and the Drive is capable of writing the media.
+	 * If the Erasable bit is set to zero, then either the medium is not erasable or the
+	 * Drive is unable to write the media.
+	 */
+	__u8 erasable : 1;
+	/**
+	 * The State of Last Session field specifies the recorded state of the last
+	 * session, regardless of the number of sessions on the disc.
+	 */
+	__u8 border_status : 2;
+	/* The Disc Status field indicates the recorded status of the disc */
+	__u8 disc_status : 2;
 #elif defined(__LITTLE_ENDIAN_BITFIELD)
-        __u8 disc_status		: 2;
-        __u8 border_status		: 2;
-        __u8 erasable			: 1;
-	__u8 reserved1			: 3;
+	__u8 disc_status : 2;
+	__u8 border_status : 2;
+	__u8 erasable : 1;
+	__u8 info_data_type : 3;
 #else
 #error "Please fix <asm/byteorder.h>"
 #endif
+	/**
+	 * The Number of First Track on Disc is the track number of the Logical Track that
+	 * contains LBA 0
+	 */
 	__u8 n_first_track;
 	__u8 n_sessions_lsb;
+	/**
+	 * First Track Number in Last Session (bytes 5 & 10) is the track number of the
+	 * first Logical Track in the last session.
+	 * This includes the incomplete logical track.
+	 */
 	__u8 first_track_lsb;
+	/**
+	 * Last Track Number in Last Session (bytes 6 & 11) is the track number of the last
+	 * Logical Track in the last session.
+	 * This includes the incomplete logical track.
+	 */
 	__u8 last_track_lsb;
 #if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 did_v			: 1;
-        __u8 dbc_v			: 1;
-        __u8 uru			: 1;
-        __u8 reserved2			: 2;
-	__u8 dbit			: 1;
-	__u8 mrw_status			: 2;
+	/**
+	 * The DID_V (Disc ID Valid) bit, when set to one, indicates that the Disc
+	 * Identification field is valid
+	 */
+	__u8 did_v : 1;
+	/**
+	 * The DBC_V (Disc Bar Code Valid bit, when set to one, indicates that the Disc Bar
+	 * Code field (bytes 24 through 31) is valid
+	 */
+	__u8 dbc_v : 1;
+	/**
+	 * The URU (Unrestricted Use Disc) bit may be zero for special use CD-R, CD-RW,
+	 * or DVD-R, medium.
+	 * For all other media types, URU shall be set to one. When URU is zero, the mounted
+	 * disc is defined for restricted use.
+	 */
+	__u8 uru : 1;
+	/**
+	 * DAC_V indicates the validity of the Disc Application Code in byte 32. If DAC_V is
+	 * set to zero, then the Disc Application Code is not valid. If DAC_V is set to one,
+	 * the Disc Application Code is valid.
+	 */
+	__u8 dac_v: 1;
+	__u8 reserved2 : 1;
+	/**
+	 * If the disc is MRW formatted or MRW formatting (state = 01b, 10b, or 11b),
+	 * then bit 2 of byte 7 (Dbit) is a copy of the “dirty bit” from the defect table.
+	 * If Dbit is set to zero, then the MRW structures are current.
+	 * If Dbit is set to one, then the MRW structures may not be current.
+	 * When BG format status = 00b, Dbit shall be set to zero.
+	 */
+	__u8 dbit : 1;
+	/**
+	 * The BG format status is the background format status of the mounted disc.
+	 * Drives that report the Formattable Feature and either the MRW Feature or the DVD+RW
+	 * Feature, or both are required to implement Background format.
+	 * For all other Drives, this field shall be @param DISC_BACK_FMT_NEITHER.
+	 */
+	__u8 mrw_status : 2;
 #elif defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8 mrw_status			: 2;
-	__u8 dbit			: 1;
-        __u8 reserved2			: 2;
-        __u8 uru			: 1;
-        __u8 dbc_v			: 1;
-	__u8 did_v			: 1;
+	__u8 mrw_status : 2;
+	__u8 dbit : 1;
+	__u8 reserved2 : 1;
+	__u8 dac_v: 1;
+	__u8 uru : 1;
+	__u8 dbc_v : 1;
+	__u8 did_v : 1;
 #endif
+	/**
+	 * The Disc Type field is associated only with CD media type
+	 */
 	__u8 disc_type;
 	__u8 n_sessions_msb;
 	__u8 first_track_msb;
 	__u8 last_track_msb;
+
+	/**
+	 * For CD-R/RW, the Disc Identification number recorded in the PMA is returned.
+	 * The Disc Identification Number is recorded in the PMA as a six-digit BCD number.
+	 * It is returned in the Disc Information Block as a 32 bit binary integer.
+	 * This value should be zero filled for all other media types.
+	 */
 	__u32 disc_id;
+	/**
+	 * The Last Session Lead-in Start Address field is dependent on medium and
+	 * recorded status.
+	 */
 	__u32 lead_in;
+	/**
+	 * The Last Possible Lead-out Start Address field is dependent on medium and
+	 * recorded status.
+	 */
 	__u32 lead_out;
+	/**
+	 * For CD, the Disc Bar Code field contains the hexadecimal value of the bar code
+	 * if the Drive has the ability to read Disc Bar Code and a bar code is present.
+	 * For all other media this field should be set to zeros.
+	 */
 	__u8 disc_bar_code[8];
+	/**
+	 *
+	 */
 	__u8 reserved3;
+	/**
+	 * The Number of OPC Tables field is the number of OPC tables that follow this field.
+	 * If OPC has not been determined for the currently mounted medium, the Number of
+	 * OPC Tables field is set to zero.
+	 * The Number of OPC Tables represents the number of disc speeds for which the OPC
+	 * values are known.
+	 * Since each OPC Table is 8 bytes in length, then the number of bytes that follow
+	 * the Number of OPC Tables field is 8 x Number of OPC Tables.
+	 */
 	__u8 n_opc;
-} disc_information;
+} __packed disc_information;
 
 typedef struct {
 	__be16 track_information_length;