Index: uspace/drv/bus/usb/usbmast/main.c
===================================================================
--- uspace/drv/bus/usb/usbmast/main.c	(revision 71fa44c5700fa50dff7a922a2ca924e7127b7f61)
+++ uspace/drv/bus/usb/usbmast/main.c	(revision 5e2aa83bfda339210f01a52684c9441d5653a50e)
@@ -138,4 +138,24 @@
 	    "block_size=%" PRIu32 "\n", nblocks, block_size);
 
+	usb_log_info("Doing test read of block 0.\n");
+	static uint8_t bdata[512];
+
+	rc = usbmast_read(dev, 0, 1, 512, &bdata);
+	if (rc != EOK) {
+		usb_log_warning("Failed to read block 0, device `%s': %s.\n",
+		    dev->ddf_dev->name, str_error(rc));
+		return EOK;
+	}
+
+	usb_log_info("Requesting sense data.\n");
+	static scsi_sense_data_t sdata;
+
+	rc = usbmast_request_sense(dev, &sdata, sizeof(sdata));
+	if (rc != EOK) {
+		usb_log_warning("Failed to get sense data, device `%s': %s.\n",
+		    dev->ddf_dev->name, str_error(rc));
+		return EOK;
+	}
+
 	return EOK;
 }
Index: uspace/drv/bus/usb/usbmast/mast.c
===================================================================
--- uspace/drv/bus/usb/usbmast/mast.c	(revision 71fa44c5700fa50dff7a922a2ca924e7127b7f61)
+++ uspace/drv/bus/usb/usbmast/mast.c	(revision 5e2aa83bfda339210f01a52684c9441d5653a50e)
@@ -92,4 +92,8 @@
 	    str_error(rc));
 	if (rc != EOK) {
+		/*
+		 * XXX If the pipe is stalled, we should clear it
+		 * and read CSW.
+		 */
 		return rc;
 	}
@@ -103,11 +107,15 @@
 	    str_error(rc));
 	if (rc != EOK) {
-		return rc;
-	}
+		MASTLOG("rc != EOK\n");
+		return rc;
+	}
+
 	if (csw_size != sizeof(csw)) {
+		MASTLOG("csw_size != sizeof(csw)\n");
 		return ERANGE;
 	}
 
 	if (csw.dCSWTag != tag) {
+		MASTLOG("csw.dCSWTag != tag\n");
 		return EBADCHECKSUM;
 	}
@@ -117,4 +125,5 @@
 	 */
 	if (csw.dCSWStatus != 0) {
+		MASTLOG("csw.dCSWStatus != 0\n");
 		// FIXME: better error code
 		// FIXME: distinguish 0x01 and 0x02
@@ -124,9 +133,15 @@
 	size_t residue = (size_t) uint32_usb2host(csw.dCSWDataResidue);
 	if (residue > in_buffer_size) {
+		MASTLOG("residue > in_buffer_size\n");
 		return ERANGE;
 	}
-	if (act_size != in_buffer_size - residue) {
-		return ERANGE;
-	}
+
+	/*
+	 * When the device has less data to send than requested, it can
+	 * either stall the pipe or send garbage and indicate that via
+	 * the residue field in CSW. That means in_buffer_size - residue
+	 * is the authoritative size of data received.
+	 */
+
 	if (received_size != NULL) {
 		*received_size = in_buffer_size - residue;
Index: uspace/drv/bus/usb/usbmast/scsi_ms.c
===================================================================
--- uspace/drv/bus/usb/usbmast/scsi_ms.c	(revision 71fa44c5700fa50dff7a922a2ca924e7127b7f61)
+++ uspace/drv/bus/usb/usbmast/scsi_ms.c	(revision 5e2aa83bfda339210f01a52684c9441d5653a50e)
@@ -38,4 +38,5 @@
 #include <byteorder.h>
 #include <inttypes.h>
+#include <macros.h>
 #include <usb/dev/driver.h>
 #include <usb/debug.h>
@@ -115,4 +116,40 @@
 }
 
+/** Perform SCSI Request Sense command on USB mass storage device.
+ *
+ * @param dev		USB device
+ * @param buf		Destination buffer
+ * @param size		Size of @a buf
+ *
+ * @return		Error code.
+ */
+int usbmast_request_sense(usb_device_t *dev, void *buf, size_t size)
+{
+	scsi_cdb_request_sense_t cdb;
+	size_t data_len;
+	int rc;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_REQUEST_SENSE;
+	cdb.alloc_len = min(size, SCSI_SENSE_DATA_MAX_SIZE);
+
+	rc = usb_massstor_data_in(dev, 0xDEADBEEF, 0, (uint8_t *) &cdb,
+	    sizeof(cdb), buf, size, &data_len);
+
+        if (rc != EOK) {
+		usb_log_error("Request Sense failed, device %s: %s.\n",
+		   dev->ddf_dev->name, str_error(rc));
+		return rc;
+	}
+
+	if (data_len < SCSI_SENSE_DATA_MIN_SIZE) {
+		/* The missing bytes should be considered to be zeroes. */
+		memset((uint8_t *)buf + data_len, 0,
+		    SCSI_SENSE_DATA_MIN_SIZE - data_len);
+	}
+
+	return EOK;
+}
+
 /** Perform SCSI Read Capacity command on USB mass storage device.
  *
@@ -155,4 +192,51 @@
 }
 
+/** Perform SCSI Read command on USB mass storage device.
+ *
+ * @param dev		USB device.
+ * @param ba		Address of first block.
+ * @param nblocks	Number of blocks to read.
+ * @param bsize		Block size.
+ *
+ * @return		Error code.
+ */
+int usbmast_read(usb_device_t *dev, uint64_t ba, size_t nblocks, size_t bsize,
+    void *buf)
+{
+	scsi_cdb_read_12_t cdb;
+	size_t data_len;
+	int rc;
+
+	/* XXX Need softstate to store block size. */
+
+	if (ba > UINT32_MAX)
+		return ELIMIT;
+
+	if ((uint64_t)nblocks * bsize > UINT32_MAX)
+		return ELIMIT;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_READ_12;
+	cdb.lba = host2uint32_t_be(ba);
+	cdb.xfer_len = host2uint32_t_be(nblocks);
+
+	rc = usb_massstor_data_in(dev, 0xDEADBEEF, 0, (uint8_t *) &cdb,
+	    sizeof(cdb), buf, nblocks * bsize, &data_len);
+
+        if (rc != EOK) {
+		usb_log_error("Read (12) failed, device %s: %s.\n",
+		   dev->ddf_dev->name, str_error(rc));
+		return rc;
+	}
+
+	if (data_len < nblocks * bsize) {
+		usb_log_error("SCSI Read response too short (%zu).\n",
+		    data_len);
+		return EIO;
+	}
+
+	return EOK;
+}
+
 /**
  * @}
Index: uspace/drv/bus/usb/usbmast/scsi_ms.h
===================================================================
--- uspace/drv/bus/usb/usbmast/scsi_ms.h	(revision 71fa44c5700fa50dff7a922a2ca924e7127b7f61)
+++ uspace/drv/bus/usb/usbmast/scsi_ms.h	(revision 5e2aa83bfda339210f01a52684c9441d5653a50e)
@@ -60,5 +60,7 @@
 
 extern int usbmast_inquiry(usb_device_t *, usbmast_inquiry_data_t *);
-extern int usbmast_read_capacity(usb_device_t *dev, uint32_t *, uint32_t *);
+extern int usbmast_request_sense(usb_device_t *, void *, size_t);
+extern int usbmast_read_capacity(usb_device_t *, uint32_t *, uint32_t *);
+extern int usbmast_read(usb_device_t *, uint64_t, size_t, size_t, void *);
 extern const char *usbmast_scsi_dev_type_str(unsigned);
 
