Index: uspace/drv/block/usbmast/Makefile
===================================================================
--- uspace/drv/block/usbmast/Makefile	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/Makefile	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,41 @@
+#
+# Copyright (c) 2011 Vojtech Horky
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+
+LIBS = usbdev usb drv scsi
+
+BINARY = usbmast
+
+SOURCES = \
+	bo_trans.c \
+	cmdw.c \
+	main.c \
+	scsi_ms.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/block/usbmast/bo_trans.c
===================================================================
--- uspace/drv/block/usbmast/bo_trans.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/bo_trans.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/**
+ * @file
+ * USB mass storage bulk-only transport.
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/dev/request.h>
+
+#include "bo_trans.h"
+#include "cmdw.h"
+#include "usbmast.h"
+
+
+#define MASTLOG(format, ...) \
+	usb_log_debug2("USB cl08: " format, ##__VA_ARGS__)
+
+/** Send command via bulk-only transport.
+ *
+ * @param mfun		Mass storage function
+ * @param tag		Command block wrapper tag (automatically compared
+ *			with answer)
+ * @param cmd		SCSI command
+ *
+ * @return		Error code
+ */
+int usb_massstor_cmd(usbmast_fun_t *mfun, uint32_t tag, scsi_cmd_t *cmd)
+{
+	int rc;
+
+	if (cmd->data_in && cmd->data_out)
+		return EINVAL;
+
+	usb_pipe_t *bulk_in_pipe = mfun->mdev->bulk_in_pipe;
+	usb_pipe_t *bulk_out_pipe = mfun->mdev->bulk_out_pipe;
+
+	usb_pipe_t *dpipe = bulk_out_pipe;
+	usb_direction_t ddir = USB_DIRECTION_OUT;
+	size_t dbuf_size = cmd->data_out_size;
+
+	if (cmd->data_in) {
+		ddir = USB_DIRECTION_IN;
+		dbuf_size = cmd->data_in_size;
+		dpipe = bulk_in_pipe;
+	}
+
+	/* Prepare CBW - command block wrapper */
+	usb_massstor_cbw_t cbw;
+	usb_massstor_cbw_prepare(&cbw, tag, dbuf_size, ddir, mfun->lun,
+	    cmd->cdb_size, cmd->cdb);
+
+	/* Send the CBW. */
+	MASTLOG("Sending CBW.\n");
+	rc = usb_pipe_write(bulk_out_pipe, &cbw, sizeof(cbw));
+	MASTLOG("CBW '%s' sent: %s.\n",
+	    usb_debug_str_buffer((uint8_t *) &cbw, sizeof(cbw), 0),
+	    str_error(rc));
+	if (rc != EOK) {
+		usb_log_error("Bulk out write failed: %s\n", str_error(rc));
+		return EIO;
+	}
+
+	MASTLOG("Transferring data.\n");
+	if (cmd->data_in) {
+		size_t act_size;
+		/* Recieve data from the device. */
+		rc = usb_pipe_read(dpipe, cmd->data_in, cmd->data_in_size,
+		    &act_size);
+		MASTLOG("Received %zu bytes (%s): %s.\n", act_size,
+		    usb_debug_str_buffer(cmd->data_in, act_size, 0),
+		    str_error(rc));
+	}
+	if (cmd->data_out) {
+		/* Send data to the device. */
+		rc = usb_pipe_write(dpipe, cmd->data_out, cmd->data_out_size);
+		MASTLOG("Sent %zu bytes (%s): %s.\n", cmd->data_out_size,
+		    usb_debug_str_buffer(cmd->data_out, cmd->data_out_size, 0),
+		    str_error(rc));
+	}
+
+	if (rc == ESTALL) {
+		/* Clear stall condition and continue below to read CSW. */
+		usb_pipe_clear_halt(
+		    usb_device_get_default_pipe(mfun->mdev->usb_dev), dpipe);
+        } else if (rc != EOK) {
+		usb_log_error("Failed to transfer data: %s", str_error(rc));
+		return EIO;
+	}
+
+	/* Read CSW. */
+	usb_massstor_csw_t csw;
+	size_t csw_size;
+	MASTLOG("Reading CSW.\n");
+	rc = usb_pipe_read(bulk_in_pipe, &csw, sizeof(csw), &csw_size);
+	MASTLOG("CSW '%s' received (%zu bytes): %s.\n",
+	    usb_debug_str_buffer((uint8_t *) &csw, csw_size, 0), csw_size,
+	    str_error(rc));
+	if (rc != EOK) {
+		usb_log_error("Failed to read CSW: %s", str_error(rc));
+		return EIO;
+	}
+
+	if (csw_size != sizeof(csw)) {
+		usb_log_error("Received CSW of incorrect size.");
+		return EIO;
+	}
+
+	if (csw.dCSWTag != tag) {
+		usb_log_error("Received CSW with incorrect tag. (expected: %"
+		    PRIX32" received: %"PRIx32, tag, csw.dCSWTag);
+		return EIO;
+	}
+
+	/*
+	 * Determine the actual return value from the CSW.
+	 */
+	switch (csw.dCSWStatus) {
+	case cbs_passed:
+		cmd->status = CMDS_GOOD;
+		break;
+	case cbs_failed:
+		cmd->status = CMDS_FAILED;
+		usb_log_error("CBS Failed.\n");
+		break;
+	case cbs_phase_error:
+		usb_log_error("CBS phase error.\n");
+		rc = EIO;
+		break;
+	default:
+		usb_log_error("CBS other error.\n");
+		rc = EIO;
+		break;
+	}
+
+	const size_t residue = uint32_usb2host(csw.dCSWDataResidue);
+	if (residue > dbuf_size) {
+		usb_log_error("Residue > buffer size (%zu > %zu).\n",
+		    residue, dbuf_size);
+		return EIO;
+	}
+
+	/*
+	 * When the device has less data to send than requested (or cannot
+	 * receive moredata), it can either stall the pipe or send garbage
+	 * (ignore data) and indicate that via the residue field in CSW.
+	 * That means dbuf_size - residue is the authoritative size of data
+	 * received (sent).
+	 */
+
+	if (cmd->data_in)
+		cmd->rcvd_size = dbuf_size - residue;
+
+	return rc;
+}
+
+/** Perform bulk-only mass storage reset.
+ *
+ * @param mfun		Mass storage function
+ * @return		Error code
+ */
+int usb_massstor_reset(usbmast_dev_t *mdev)
+{
+	return usb_control_request_set(
+	    usb_device_get_default_pipe(mdev->usb_dev),
+	    USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE,
+	    0xFF, 0, usb_device_get_iface_number(mdev->usb_dev), NULL, 0);
+}
+
+/** Perform complete reset recovery of bulk-only mass storage.
+ *
+ * Notice that no error is reported because if this fails, the error
+ * would reappear on next transaction somehow.
+ *
+ * @param mfun		Mass storage function
+ */
+void usb_massstor_reset_recovery(usbmast_dev_t *mdev)
+{
+	/* We would ignore errors here because if this fails
+	 * we are doomed anyway and any following transaction would fail.
+	 */
+	usb_massstor_reset(mdev);
+	usb_pipe_clear_halt(usb_device_get_default_pipe(mdev->usb_dev),
+	    mdev->bulk_in_pipe);
+	usb_pipe_clear_halt(usb_device_get_default_pipe(mdev->usb_dev),
+	    mdev->bulk_out_pipe);
+}
+
+/** Get max LUN of a mass storage device.
+ *
+ * @see usb_masstor_get_lun_count
+ *
+ * @warning Error from this command does not necessarily indicate malfunction
+ * of the device. Device does not need to support this request.
+ * You shall rather use usb_masstor_get_lun_count.
+ *
+ * @param mfun		Mass storage function
+ * @return		Error code of maximum LUN (index, not count)
+ */
+int usb_massstor_get_max_lun(usbmast_dev_t *mdev)
+{
+	uint8_t max_lun;
+	size_t data_recv_len;
+	int rc = usb_control_request_get(
+	    usb_device_get_default_pipe(mdev->usb_dev),
+	    USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_INTERFACE,
+	    0xFE, 0, usb_device_get_iface_number(mdev->usb_dev), &max_lun, 1,
+	    &data_recv_len);
+	if (rc != EOK) {
+		return rc;
+	}
+	if (data_recv_len != 1) {
+		return EEMPTY;
+	}
+	return (int) max_lun;
+}
+
+/** Get number of LUNs supported by mass storage device.
+ *
+ * @warning This function hides any error during the request
+ * (typically that shall not be a problem).
+ *
+ * @param mfun		Mass storage function
+ * @return		Number of LUNs
+ */
+size_t usb_masstor_get_lun_count(usbmast_dev_t *mdev)
+{
+	int max_lun = usb_massstor_get_max_lun(mdev);
+	if (max_lun < 0) {
+		max_lun = 1;
+	} else {
+		max_lun++;
+	}
+
+	return (size_t) max_lun;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/bo_trans.h
===================================================================
--- uspace/drv/block/usbmast/bo_trans.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/bo_trans.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/** @file
+ * USB mass storage bulk-only transport.
+ */
+
+#ifndef BO_TRANS_H_
+#define BO_TRANS_H_
+
+#include <scsi/spc.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <usb/usb.h>
+#include <usb/dev/pipes.h>
+#include <usb/dev/driver.h>
+#include "usbmast.h"
+
+typedef enum cmd_status {
+	CMDS_GOOD,
+	CMDS_FAILED
+} cmd_status_t;
+
+/** SCSI command.
+ *
+ * Contains (a subset of) the input and output arguments of SCSI
+ * Execute Command procedure call (see SAM-4 chapter 5.1)
+ */
+typedef struct {
+	/*
+	 * Related to IN fields
+	 */
+
+	/** Command Descriptor Block */
+	const void *cdb;
+	/** CDB size in bytes */
+	size_t cdb_size;
+
+	/** Outgoing data */
+	const void *data_out;
+	/** Size of outgoing data in bytes */
+	size_t data_out_size;
+
+	/*
+	 * Related to OUT fields
+	 */
+
+	/** Buffer for incoming data */
+	void *data_in;
+	/** Size of input buffer in bytes */
+	size_t data_in_size;
+
+	/** Number of bytes actually received */
+	size_t rcvd_size;
+
+	/** Status */
+	cmd_status_t status;
+} scsi_cmd_t;
+
+extern int usb_massstor_cmd(usbmast_fun_t *, uint32_t, scsi_cmd_t *);
+extern int usb_massstor_reset(usbmast_dev_t *);
+extern void usb_massstor_reset_recovery(usbmast_dev_t *);
+extern int usb_massstor_get_max_lun(usbmast_dev_t *);
+extern size_t usb_masstor_get_lun_count(usbmast_dev_t *);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/cmdw.c
===================================================================
--- uspace/drv/block/usbmast/cmdw.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/cmdw.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/** @file
+ * USB mass storage commands.
+ */
+
+#include <byteorder.h>
+#include <mem.h>
+#include <stdint.h>
+#include <usb/usb.h>
+#include "cmdw.h"
+
+void usb_massstor_cbw_prepare(usb_massstor_cbw_t *cbw,
+    uint32_t tag, uint32_t transfer_length, usb_direction_t dir,
+    uint8_t lun, uint8_t cmd_len, const uint8_t *cmd)
+{
+	cbw->dCBWSignature = uint32_host2usb(0x43425355);
+	cbw->dCBWTag = tag;
+	cbw->dCBWDataTransferLength = transfer_length;
+
+	cbw->bmCBWFlags = 0;
+	if (dir == USB_DIRECTION_IN) {
+		cbw->bmCBWFlags |= (1 << 7);
+	}
+
+	/* Only lowest 4 bits. */
+	cbw->bCBWLUN = lun & 0x0F;
+
+	/* Only lowest 5 bits. */
+	cbw->bCBWBLength = cmd_len & 0x1F;
+
+	memcpy(cbw->CBWCB, cmd, cbw->bCBWBLength);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/cmdw.h
===================================================================
--- uspace/drv/block/usbmast/cmdw.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/cmdw.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/** @file
+ * USB mass storage commands.
+ */
+
+#ifndef CMDW_H_
+#define CMDW_H_
+
+#include <stdint.h>
+#include <usb/usb.h>
+
+typedef struct {
+	uint32_t dCBWSignature;
+	uint32_t dCBWTag;
+	uint32_t dCBWDataTransferLength;
+	uint8_t bmCBWFlags;
+	uint8_t bCBWLUN;
+	uint8_t bCBWBLength;
+	uint8_t CBWCB[16];
+} __attribute__((packed)) usb_massstor_cbw_t;
+
+typedef struct {
+	uint32_t dCSWSignature;
+	uint32_t dCSWTag;
+	uint32_t dCSWDataResidue;
+	uint8_t dCSWStatus;
+} __attribute__((packed)) usb_massstor_csw_t;
+
+enum cmd_block_status {
+	cbs_passed	= 0x00,
+	cbs_failed	= 0x01,
+	cbs_phase_error = 0x02
+};
+
+extern void usb_massstor_cbw_prepare(usb_massstor_cbw_t *, uint32_t, uint32_t,
+    usb_direction_t, uint8_t, uint8_t, const uint8_t *);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/main.c
===================================================================
--- uspace/drv/block/usbmast/main.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/main.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/**
+ * @file
+ * Main routines of USB mass storage driver.
+ */
+
+#include <as.h>
+#include <async.h>
+#include <bd_srv.h>
+#include <macros.h>
+#include <usb/dev/driver.h>
+#include <usb/debug.h>
+#include <usb/classes/classes.h>
+#include <usb/classes/massstor.h>
+#include <errno.h>
+#include <io/logctl.h>
+#include <str_error.h>
+#include "cmdw.h"
+#include "bo_trans.h"
+#include "scsi_ms.h"
+#include "usbmast.h"
+
+#define NAME "usbmast"
+
+static const usb_endpoint_description_t bulk_in_ep = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_IN,
+	.interface_class = USB_CLASS_MASS_STORAGE,
+	.interface_subclass = USB_MASSSTOR_SUBCLASS_SCSI,
+	.interface_protocol = USB_MASSSTOR_PROTOCOL_BBB,
+	.flags = 0
+};
+static const usb_endpoint_description_t bulk_out_ep = {
+	.transfer_type = USB_TRANSFER_BULK,
+	.direction = USB_DIRECTION_OUT,
+	.interface_class = USB_CLASS_MASS_STORAGE,
+	.interface_subclass = USB_MASSSTOR_SUBCLASS_SCSI,
+	.interface_protocol = USB_MASSSTOR_PROTOCOL_BBB,
+	.flags = 0
+};
+
+static const usb_endpoint_description_t *mast_endpoints[] = {
+	&bulk_in_ep,
+	&bulk_out_ep,
+	NULL
+};
+
+static int usbmast_fun_create(usbmast_dev_t *mdev, unsigned lun);
+static void usbmast_bd_connection(ipc_callid_t iid, ipc_call_t *icall,
+    void *arg);
+
+static int usbmast_bd_open(bd_srvs_t *, bd_srv_t *);
+static int usbmast_bd_close(bd_srv_t *);
+static int usbmast_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int usbmast_bd_sync_cache(bd_srv_t *, aoff64_t, size_t);
+static int usbmast_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+static int usbmast_bd_get_block_size(bd_srv_t *, size_t *);
+static int usbmast_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static bd_ops_t usbmast_bd_ops = {
+	.open = usbmast_bd_open,
+	.close = usbmast_bd_close,
+	.read_blocks = usbmast_bd_read_blocks,
+	.sync_cache = usbmast_bd_sync_cache,
+	.write_blocks = usbmast_bd_write_blocks,
+	.get_block_size = usbmast_bd_get_block_size,
+	.get_num_blocks = usbmast_bd_get_num_blocks
+};
+
+static usbmast_fun_t *bd_srv_usbmast(bd_srv_t *bd)
+{
+	return (usbmast_fun_t *) bd->srvs->sarg;
+}
+
+/** Callback when a device is removed from the system.
+ *
+ * @param dev Representation of USB device.
+ * @return Error code.
+ */
+static int usbmast_device_gone(usb_device_t *dev)
+{
+	usbmast_dev_t *mdev = usb_device_data_get(dev);
+	assert(mdev);
+
+	for (size_t i = 0; i < mdev->lun_count; ++i) {
+		const int rc = ddf_fun_unbind(mdev->luns[i]);
+		if (rc != EOK) {
+			usb_log_error("Failed to unbind LUN function %zu: "
+			    "%s\n", i, str_error(rc));
+			return rc;
+		}
+		ddf_fun_destroy(mdev->luns[i]);
+		mdev->luns[i] = NULL;
+	}
+	free(mdev->luns);
+	return EOK;
+}
+
+/** Callback when a device is about to be removed.
+ *
+ * @param dev Representation of USB device.
+ * @return Error code.
+ */
+static int usbmast_device_remove(usb_device_t *dev)
+{
+	//TODO: flush buffers, or whatever.
+	//TODO: remove device
+	return ENOTSUP;
+}
+
+/** Callback when new device is attached and recognized as a mass storage.
+ *
+ * @param dev Representation of USB device.
+ * @return Error code.
+ */
+static int usbmast_device_add(usb_device_t *dev)
+{
+	int rc;
+	usbmast_dev_t *mdev = NULL;
+	unsigned i;
+
+	usb_endpoint_mapping_t *epm_in =
+	    usb_device_get_mapped_ep_desc(dev, &bulk_in_ep);
+	usb_endpoint_mapping_t *epm_out =
+	    usb_device_get_mapped_ep_desc(dev, &bulk_out_ep);
+	if (!epm_in || !epm_out || !epm_in->present || !epm_out->present) {
+		usb_log_error("Required EPs were not mapped.\n");
+		return ENOENT;
+	}
+
+	/* Allocate softstate */
+	mdev = usb_device_data_alloc(dev, sizeof(usbmast_dev_t));
+	if (mdev == NULL) {
+		usb_log_error("Failed allocating softstate.\n");
+		return ENOMEM;
+	}
+
+	mdev->usb_dev = dev;
+
+	usb_log_info("Initializing mass storage `%s'.\n",
+	    usb_device_get_name(dev));
+	usb_log_debug("Bulk in endpoint: %d [%zuB].\n",
+	    epm_in->pipe.endpoint_no, epm_in->pipe.max_packet_size);
+	usb_log_debug("Bulk out endpoint: %d [%zuB].\n",
+	    epm_out->pipe.endpoint_no, epm_out->pipe.max_packet_size);
+
+	usb_log_debug("Get LUN count...\n");
+	mdev->lun_count = usb_masstor_get_lun_count(mdev);
+	mdev->luns = calloc(mdev->lun_count, sizeof(ddf_fun_t*));
+	if (mdev->luns == NULL) {
+		rc = ENOMEM;
+		usb_log_error("Failed allocating luns table.\n");
+		goto error;
+	}
+
+	mdev->bulk_in_pipe = &epm_in->pipe;
+	mdev->bulk_out_pipe = &epm_out->pipe;
+	for (i = 0; i < mdev->lun_count; i++) {
+		rc = usbmast_fun_create(mdev, i);
+		if (rc != EOK)
+			goto error;
+	}
+
+	return EOK;
+error:
+	/* Destroy functions */
+	for (size_t i = 0; i < mdev->lun_count; ++i) {
+		if (mdev->luns[i] == NULL)
+			continue;
+		const int rc = ddf_fun_unbind(mdev->luns[i]);
+		if (rc != EOK) {
+			usb_log_warning("Failed to unbind LUN function %zu: "
+			    "%s.\n", i, str_error(rc));
+		}
+		ddf_fun_destroy(mdev->luns[i]);
+	}
+	free(mdev->luns);
+	return rc;
+}
+
+/** Create mass storage function.
+ *
+ * Called once for each LUN.
+ *
+ * @param mdev		Mass storage device
+ * @param lun		LUN
+ * @return		EOK on success or negative error code.
+ */
+static int usbmast_fun_create(usbmast_dev_t *mdev, unsigned lun)
+{
+	int rc;
+	char *fun_name = NULL;
+	ddf_fun_t *fun = NULL;
+	usbmast_fun_t *mfun = NULL;
+
+	if (asprintf(&fun_name, "l%u", lun) < 0) {
+		usb_log_error("Out of memory.\n");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	fun = usb_device_ddf_fun_create(mdev->usb_dev, fun_exposed, fun_name);
+	if (fun == NULL) {
+		usb_log_error("Failed to create DDF function %s.\n", fun_name);
+		rc = ENOMEM;
+		goto error;
+	}
+
+	/* Allocate soft state */
+	mfun = ddf_fun_data_alloc(fun, sizeof(usbmast_fun_t));
+	if (mfun == NULL) {
+		usb_log_error("Failed allocating softstate.\n");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	mfun->ddf_fun = fun;
+	mfun->mdev = mdev;
+	mfun->lun = lun;
+
+	bd_srvs_init(&mfun->bds);
+	mfun->bds.ops = &usbmast_bd_ops;
+	mfun->bds.sarg = mfun;
+
+	/* Set up a connection handler. */
+	ddf_fun_set_conn_handler(fun, usbmast_bd_connection);
+
+	usb_log_debug("Inquire...\n");
+	usbmast_inquiry_data_t inquiry;
+	rc = usbmast_inquiry(mfun, &inquiry);
+	if (rc != EOK) {
+		usb_log_warning("Failed to inquire device `%s': %s.\n",
+		    usb_device_get_name(mdev->usb_dev), str_error(rc));
+		rc = EIO;
+		goto error;
+	}
+
+	usb_log_info("Mass storage `%s' LUN %u: " \
+	    "%s by %s rev. %s is %s (%s).\n",
+	    usb_device_get_name(mdev->usb_dev),
+	    lun,
+	    inquiry.product,
+	    inquiry.vendor,
+	    inquiry.revision,
+	    usbmast_scsi_dev_type_str(inquiry.device_type),
+	    inquiry.removable ? "removable" : "non-removable");
+
+	uint32_t nblocks, block_size;
+
+	rc = usbmast_read_capacity(mfun, &nblocks, &block_size);
+	if (rc != EOK) {
+		usb_log_warning("Failed to read capacity, device `%s': %s.\n",
+		    usb_device_get_name(mdev->usb_dev), str_error(rc));
+		rc = EIO;
+		goto error;
+	}
+
+	usb_log_info("Read Capacity: nblocks=%" PRIu32 ", "
+	    "block_size=%" PRIu32 "\n", nblocks, block_size);
+
+	mfun->nblocks = nblocks;
+	mfun->block_size = block_size;
+
+	rc = ddf_fun_bind(fun);
+	if (rc != EOK) {
+		usb_log_error("Failed to bind DDF function %s: %s.\n",
+		    fun_name, str_error(rc));
+		goto error;
+	}
+
+	ddf_fun_add_to_category(fun, "disk");
+
+	free(fun_name);
+	mdev->luns[lun] = fun;
+
+	return EOK;
+
+	/* Error cleanup */
+error:
+	if (fun != NULL)
+		ddf_fun_destroy(fun);
+	if (fun_name != NULL)
+		free(fun_name);
+	return rc;
+}
+
+/** Blockdev client connection handler. */
+static void usbmast_bd_connection(ipc_callid_t iid, ipc_call_t *icall,
+    void *arg)
+{
+	usbmast_fun_t *mfun;
+
+	mfun = (usbmast_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
+	bd_conn(iid, icall, &mfun->bds);
+}
+
+/** Open device. */
+static int usbmast_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Close device. */
+static int usbmast_bd_close(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Read blocks from the device. */
+static int usbmast_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt, void *buf,
+    size_t size)
+{
+	usbmast_fun_t *mfun = bd_srv_usbmast(bd);
+
+	if (size < cnt * mfun->block_size)
+		return EINVAL;
+
+	return usbmast_read(mfun, ba, cnt, buf);
+}
+
+/** Synchronize blocks to nonvolatile storage. */
+static int usbmast_bd_sync_cache(bd_srv_t *bd, uint64_t ba, size_t cnt)
+{
+	usbmast_fun_t *mfun = bd_srv_usbmast(bd);
+
+	return usbmast_sync_cache(mfun, ba, cnt);
+}
+
+/** Write blocks to the device. */
+static int usbmast_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	usbmast_fun_t *mfun = bd_srv_usbmast(bd);
+
+	if (size < cnt * mfun->block_size)
+		return EINVAL;
+
+	return usbmast_write(mfun, ba, cnt, buf);
+}
+
+/** Get device block size. */
+static int usbmast_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	usbmast_fun_t *mfun = bd_srv_usbmast(bd);
+	*rsize = mfun->block_size;
+	return EOK;
+}
+
+/** Get number of blocks on device. */
+static int usbmast_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	usbmast_fun_t *mfun = bd_srv_usbmast(bd);
+	*rnb = mfun->nblocks;
+	return EOK;
+}
+
+
+/** USB mass storage driver ops. */
+static const usb_driver_ops_t usbmast_driver_ops = {
+	.device_add = usbmast_device_add,
+	.device_rem = usbmast_device_remove,
+	.device_gone = usbmast_device_gone,
+};
+
+/** USB mass storage driver. */
+static const usb_driver_t usbmast_driver = {
+	.name = NAME,
+	.ops = &usbmast_driver_ops,
+	.endpoints = mast_endpoints
+};
+
+int main(int argc, char *argv[])
+{
+	log_init(NAME);
+	logctl_set_log_level(NAME, LVL_NOTE);
+	return usb_driver_main(&usbmast_driver);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/scsi_ms.c
===================================================================
--- uspace/drv/block/usbmast/scsi_ms.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/scsi_ms.c	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/**
+ * @file
+ * SCSI functions for USB mass storage driver.
+ */
+
+#include <bitops.h>
+#include <byteorder.h>
+#include <inttypes.h>
+#include <macros.h>
+#include <usb/dev/driver.h>
+#include <usb/debug.h>
+#include <errno.h>
+#include <str_error.h>
+#include <str.h>
+#include <scsi/sbc.h>
+#include <scsi/spc.h>
+#include "cmdw.h"
+#include "bo_trans.h"
+#include "scsi_ms.h"
+#include "usbmast.h"
+
+/** Get string representation for SCSI peripheral device type.
+ *
+ * @param type		SCSI peripheral device type code.
+ * @return		String representation.
+ */
+const char *usbmast_scsi_dev_type_str(unsigned type)
+{
+	return scsi_get_dev_type_str(type);
+}
+
+static void usbmast_dump_sense(scsi_sense_data_t *sense_buf)
+{
+	const unsigned sense_key = sense_buf->flags_key & 0x0f;
+	printf("Got sense data. Sense key: 0x%x (%s), ASC 0x%02x, "
+	    "ASCQ 0x%02x.\n", sense_key,
+	    scsi_get_sense_key_str(sense_key),
+	    sense_buf->additional_code,
+	    sense_buf->additional_cqual);
+}
+
+static int usb_massstor_unit_ready(usbmast_fun_t *mfun)
+{
+	scsi_cmd_t cmd;
+	scsi_cdb_test_unit_ready_t cdb;
+	int rc;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_TEST_UNIT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+
+	rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
+
+        if (rc != EOK) {
+		usb_log_error("Test Unit Ready failed on device %s: %s.",
+		   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+		return rc;
+	}
+	/* Ignore command error here. If there's something wrong
+	 * with the device the following commands will fail too.
+	 */
+	if (cmd.status != CMDS_GOOD)
+		usb_log_warning("Test Unit Ready command failed on device %s.",
+		   usb_device_get_name(mfun->mdev->usb_dev));
+
+	return EOK;
+}
+
+/** Run SCSI command.
+ *
+ * Run command and repeat in case of unit attention.
+ * XXX This is too simplified.
+ */
+static int usbmast_run_cmd(usbmast_fun_t *mfun, scsi_cmd_t *cmd)
+{
+	uint8_t sense_key;
+	scsi_sense_data_t sense_buf;
+	int rc;
+
+	do {
+		rc = usb_massstor_unit_ready(mfun);
+		if (rc != EOK) {
+			usb_log_error("Inquiry transport failed, device %s: %s.\n",
+			   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+			return rc;
+		}
+
+		rc = usb_massstor_cmd(mfun, 0xDEADBEEF, cmd);
+		if (rc != EOK) {
+			usb_log_error("Inquiry transport failed, device %s: %s.\n",
+			   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+			return rc;
+		}
+
+		if (cmd->status == CMDS_GOOD)
+			return EOK;
+
+		usb_log_error("SCSI command failed, device %s.\n",
+		    usb_device_get_name(mfun->mdev->usb_dev));
+
+		rc = usbmast_request_sense(mfun, &sense_buf, sizeof(sense_buf));
+		if (rc != EOK) {
+			usb_log_error("Failed to read sense data.\n");
+			return EIO;
+		}
+
+		/* Dump sense data to log */
+		usbmast_dump_sense(&sense_buf);
+
+		/* Get sense key */
+		sense_key = sense_buf.flags_key & 0x0f;
+
+		if (sense_key == SCSI_SK_UNIT_ATTENTION) {
+			printf("Got unit attention. Re-trying command.\n");
+		}
+
+	} while (sense_key == SCSI_SK_UNIT_ATTENTION);
+
+	/* Command status is not good, nevertheless transport succeeded. */
+	return EOK;
+}
+
+/** Perform SCSI Inquiry command on USB mass storage device.
+ *
+ * @param mfun		Mass storage function
+ * @param inquiry_result Where to store parsed inquiry result
+ * @return		Error code
+ */
+int usbmast_inquiry(usbmast_fun_t *mfun, usbmast_inquiry_data_t *inq_res)
+{
+	scsi_std_inquiry_data_t inq_data;
+	scsi_cmd_t cmd;
+	scsi_cdb_inquiry_t cdb;
+	int rc;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_INQUIRY;
+	cdb.alloc_len = host2uint16_t_be(sizeof(inq_data));
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = &inq_data;
+	cmd.data_in_size = sizeof(inq_data);
+
+	rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
+
+	if (rc != EOK) {
+		usb_log_error("Inquiry transport failed, device %s: %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Inquiry command failed, device %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev));
+		return EIO;
+	}
+
+	if (cmd.rcvd_size < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
+		usb_log_error("SCSI Inquiry response too short (%zu).\n",
+		    cmd.rcvd_size);
+		return EIO;
+	}
+
+	/*
+	 * Parse inquiry data and fill in the result structure.
+	 */
+
+	memset(inq_res, 0, sizeof(*inq_res));
+
+	inq_res->device_type = BIT_RANGE_EXTRACT(uint8_t,
+	    inq_data.pqual_devtype, SCSI_PQDT_DEV_TYPE_h, SCSI_PQDT_DEV_TYPE_l);
+
+	inq_res->removable = BIT_RANGE_EXTRACT(uint8_t,
+	    inq_data.rmb, SCSI_RMB_RMB, SCSI_RMB_RMB);
+
+	spascii_to_str(inq_res->vendor, SCSI_INQ_VENDOR_STR_BUFSIZE,
+	    inq_data.vendor, sizeof(inq_data.vendor));
+
+	spascii_to_str(inq_res->product, SCSI_INQ_PRODUCT_STR_BUFSIZE,
+	    inq_data.product, sizeof(inq_data.product));
+
+	spascii_to_str(inq_res->revision, SCSI_INQ_REVISION_STR_BUFSIZE,
+	    inq_data.revision, sizeof(inq_data.revision));
+
+	return EOK;
+}
+
+/** Perform SCSI Request Sense command on USB mass storage device.
+ *
+ * @param mfun		Mass storage function
+ * @param buf		Destination buffer
+ * @param size		Size of @a buf
+ *
+ * @return		Error code.
+ */
+int usbmast_request_sense(usbmast_fun_t *mfun, void *buf, size_t size)
+{
+	scsi_cmd_t cmd;
+	scsi_cdb_request_sense_t cdb;
+	int rc;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_REQUEST_SENSE;
+	cdb.alloc_len = min(size, SCSI_SENSE_DATA_MAX_SIZE);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = buf;
+	cmd.data_in_size = size;
+
+	rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
+
+        if (rc != EOK || cmd.status != CMDS_GOOD) {
+		usb_log_error("Request Sense failed, device %s: %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+		return rc;
+	}
+
+	if (cmd.rcvd_size < SCSI_SENSE_DATA_MIN_SIZE) {
+		/* The missing bytes should be considered to be zeroes. */
+		memset((uint8_t *)buf + cmd.rcvd_size, 0,
+		    SCSI_SENSE_DATA_MIN_SIZE - cmd.rcvd_size);
+	}
+
+	return EOK;
+}
+
+/** Perform SCSI Read Capacity command on USB mass storage device.
+ *
+ * @param mfun		Mass storage function
+ * @param nblocks	Output, number of blocks
+ * @param block_size	Output, block size in bytes
+ *
+ * @return		Error code.
+ */
+int usbmast_read_capacity(usbmast_fun_t *mfun, uint32_t *nblocks,
+    uint32_t *block_size)
+{
+	scsi_cmd_t cmd;
+	scsi_cdb_read_capacity_10_t cdb;
+	scsi_read_capacity_10_data_t data;
+	int rc;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_READ_CAPACITY_10;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = &data;
+	cmd.data_in_size = sizeof(data);
+
+	rc = usbmast_run_cmd(mfun, &cmd);
+
+        if (rc != EOK) {
+		usb_log_error("Read Capacity (10) transport failed, device %s: %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Read Capacity (10) command failed, device %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev));
+		return EIO;
+	}
+
+	if (cmd.rcvd_size < sizeof(data)) {
+		usb_log_error("SCSI Read Capacity response too short (%zu).\n",
+		    cmd.rcvd_size);
+		return EIO;
+	}
+
+	*nblocks = uint32_t_be2host(data.last_lba) + 1;
+	*block_size = uint32_t_be2host(data.block_size);
+
+	return EOK;
+}
+
+/** Perform SCSI Read command on USB mass storage device.
+ *
+ * @param mfun		Mass storage function
+ * @param ba		Address of first block
+ * @param nblocks	Number of blocks to read
+ *
+ * @return		Error code
+ */
+int usbmast_read(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks, void *buf)
+{
+	scsi_cmd_t cmd;
+	scsi_cdb_read_10_t cdb;
+	int rc;
+
+	if (ba > UINT32_MAX)
+		return ELIMIT;
+
+	if (nblocks > UINT16_MAX)
+		return ELIMIT;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_READ_10;
+	cdb.lba = host2uint32_t_be(ba);
+	cdb.xfer_len = host2uint16_t_be(nblocks);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = buf;
+	cmd.data_in_size = nblocks * mfun->block_size;
+
+	rc = usbmast_run_cmd(mfun, &cmd);
+
+        if (rc != EOK) {
+		usb_log_error("Read (10) transport failed, device %s: %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Read (10) command failed, device %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev));
+		return EIO;
+	}
+
+	if (cmd.rcvd_size < nblocks * mfun->block_size) {
+		usb_log_error("SCSI Read response too short (%zu).\n",
+		    cmd.rcvd_size);
+		return EIO;
+	}
+
+	return EOK;
+}
+
+/** Perform SCSI Write command on USB mass storage device.
+ *
+ * @param mfun		Mass storage function
+ * @param ba		Address of first block
+ * @param nblocks	Number of blocks to read
+ * @param data		Data to write
+ *
+ * @return		Error code
+ */
+int usbmast_write(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks,
+    const void *data)
+{
+	scsi_cmd_t cmd;
+	scsi_cdb_write_10_t cdb;
+	int rc;
+
+	if (ba > UINT32_MAX)
+		return ELIMIT;
+
+	if (nblocks > UINT16_MAX)
+		return ELIMIT;
+
+	memset(&cdb, 0, sizeof(cdb));
+	cdb.op_code = SCSI_CMD_WRITE_10;
+	cdb.lba = host2uint32_t_be(ba);
+	cdb.xfer_len = host2uint16_t_be(nblocks);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_out = data;
+	cmd.data_out_size = nblocks * mfun->block_size;
+
+	rc = usbmast_run_cmd(mfun, &cmd);
+
+        if (rc != EOK) {
+		usb_log_error("Write (10) transport failed, device %s: %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Write (10) command failed, device %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev));
+		return EIO;
+	}
+
+	return EOK;
+}
+
+/** Perform SCSI Synchronize Cache command on USB mass storage device.
+ *
+ * @param mfun		Mass storage function
+ * @param ba		Address of first block
+ * @param nblocks	Number of blocks to read
+ * @param data		Data to write
+ *
+ * @return		Error code
+ */
+int usbmast_sync_cache(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks)
+{
+	if (ba > UINT32_MAX)
+		return ELIMIT;
+
+	if (nblocks > UINT16_MAX)
+		return ELIMIT;
+
+	const scsi_cdb_sync_cache_10_t cdb = {
+		.op_code = SCSI_CMD_SYNC_CACHE_10,
+		.lba = host2uint32_t_be(ba),
+		.numlb = host2uint16_t_be(nblocks),
+	};
+
+	scsi_cmd_t cmd = {
+		.cdb = &cdb,
+		.cdb_size = sizeof(cdb),
+	};
+
+	const int rc = usbmast_run_cmd(mfun, &cmd);
+
+        if (rc != EOK) {
+		usb_log_error("Synchronize Cache (10) transport failed, device %s: %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev), str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Synchronize Cache (10) command failed, device %s.\n",
+		   usb_device_get_name(mfun->mdev->usb_dev));
+		return EIO;
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/scsi_ms.h
===================================================================
--- uspace/drv/block/usbmast/scsi_ms.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/scsi_ms.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/** @file
+ * SCSI functions for USB mass storage.
+ */
+
+#ifndef USB_USBMAST_SCSI_MS_H_
+#define USB_USBMAST_SCSI_MS_H_
+
+#include <scsi/spc.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <usb/usb.h>
+#include <usb/dev/driver.h>
+
+/** Result of SCSI Inquiry command.
+ * This is already parsed structure, not the original buffer returned by
+ * the device.
+ */
+typedef struct {
+	/** SCSI peripheral device type */
+	unsigned device_type;
+	/** Whether the device is removable */
+	bool removable;
+	/** Vendor ID string */
+	char vendor[SCSI_INQ_VENDOR_STR_BUFSIZE];
+	/** Product ID string */
+	char product[SCSI_INQ_PRODUCT_STR_BUFSIZE];
+	/** Revision string */
+	char revision[SCSI_INQ_REVISION_STR_BUFSIZE];
+} usbmast_inquiry_data_t;
+
+extern int usbmast_inquiry(usbmast_fun_t *, usbmast_inquiry_data_t *);
+extern int usbmast_request_sense(usbmast_fun_t *, void *, size_t);
+extern int usbmast_read_capacity(usbmast_fun_t *, uint32_t *, uint32_t *);
+extern int usbmast_read(usbmast_fun_t *, uint64_t, size_t, void *);
+extern int usbmast_write(usbmast_fun_t *, uint64_t, size_t, const void *);
+extern int usbmast_sync_cache(usbmast_fun_t *, uint64_t, size_t);
+extern const char *usbmast_scsi_dev_type_str(unsigned);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/usbmast.h
===================================================================
--- uspace/drv/block/usbmast/usbmast.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/usbmast.h	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbmast
+ * @{
+ */
+/** @file
+ * USB mass storage commands.
+ */
+
+#ifndef USBMAST_H_
+#define USBMAST_H_
+
+#include <bd_srv.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <usb/usb.h>
+
+/** Mass storage device. */
+typedef struct usbmast_dev {
+	/** USB device */
+	usb_device_t *usb_dev;
+	/** Number of LUNs */
+	unsigned lun_count;
+	/** LUN functions */
+	ddf_fun_t **luns;
+	/** Data read pipe */
+	usb_pipe_t *bulk_in_pipe;
+	/** Data write pipe */
+	usb_pipe_t *bulk_out_pipe;
+} usbmast_dev_t;
+
+
+/** Mass storage function.
+ *
+ * Serves as soft state for function/LUN.
+ */
+typedef struct {
+	/** Mass storage device the function belongs to */
+	usbmast_dev_t *mdev;
+	/** DDF function */
+	ddf_fun_t *ddf_fun;
+	/** LUN */
+	unsigned lun;
+	/** Total number of blocks */
+	uint64_t nblocks;
+	/** Block size in bytes */
+	size_t block_size;
+	/** Block device service structure */
+	bd_srvs_t bds;
+} usbmast_fun_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/block/usbmast/usbmast.ma
===================================================================
--- uspace/drv/block/usbmast/usbmast.ma	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
+++ uspace/drv/block/usbmast/usbmast.ma	(revision 493f1beab3cdc0d5615bc4f2b6e5946e6bf3270e)
@@ -0,0 +1,1 @@
+50 usb&interface&class=mass-storage&subclass=0x06&protocol=0x50
