diff --git a/repos/os/run/usb_block.run b/repos/os/run/usb_block.run
new file mode 100644
index 000000000..6acd5632c
--- /dev/null
+++ b/repos/os/run/usb_block.run
@@ -0,0 +1,161 @@
+set use_qemu [have_include "power_on/qemu"]
+
+#
+# Build
+#
+
+set build_components {
+ core init
+ drivers/timer
+ drivers/usb
+ drivers/usb_block
+ server/report_rom
+ test/blk/cli
+ test/blk/bench
+}
+
+lappend_if [have_spec gpio] build_components drivers/gpio
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_platform_drv_config
+
+append_if [have_spec gpio] config {
+
+
+
+
+ }
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+append_if [expr !$use_qemu] config {
+
+
+
+
+
+
+
+
+
+
+
+}
+append_if $use_qemu config {
+ }
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+#
+# Boot modules
+#
+
+# generic modules
+set boot_modules {
+ core init timer report_rom usb_drv usb_block_drv
+ test-blk-cli test-blk-bench
+ ld.lib.so libc.lib.so
+}
+
+append_platform_drv_boot_modules
+
+build_boot_image $boot_modules
+
+#
+# Execute test case
+#
+set disk_image "bin/test.img"
+set cmd "dd if=/dev/zero of=$disk_image bs=1M count=16"
+if {$use_qemu} {
+ puts "creating disk image:\n$cmd"
+ catch { exec sh -c $cmd }
+}
+
+#
+# Qemu opts for EHCI
+#
+append qemu_args " -m 128 -nographic -M pc -boot order=d "
+append qemu_args " -drive if=none,id=disk,file=$disk_image "
+append qemu_args " -device usb-ehci,id=ehci -device usb-storage,bus=ehci.0,drive=disk "
+
+run_genode_until {.*child "test-usb" exited with exit value 0.*} 100
diff --git a/repos/os/src/drivers/usb_block/README b/repos/os/src/drivers/usb_block/README
new file mode 100644
index 000000000..0190d8149
--- /dev/null
+++ b/repos/os/src/drivers/usb_block/README
@@ -0,0 +1,71 @@
+This directory contains an USB Mass Storage Bulk-Only driver. It uses
+the Usb session interface to access the USB device and provides Genode's
+Block service to its client.
+
+Behavior
+--------
+
+This driver only supports USB Mass Storage Bulk-Only devices that use the
+SCSI Block Commands set (direct-access). Devices using different command
+sets, e.g, SD/HC devices or some external disc drives will not work properly
+if at all. The following configuration snippets demonstrates how to use the
+driver:
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+The drivers 'config' node features a few attributes. First, there is the 'label'
+attribute. This attribute specifies the label used when opening the Usb session
+connection. A matching policy has to be configured at the USB host controller
+driver:
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+For static configurations the 'label' value may be chosen freely or may even
+be ommitted entirely. On the other hand it is best for dynamic configurations
+to choose a unique label, e.g. 'usb--', at run-time (this most likely
+involves other components that generate configurations for the 'usb_block_drv'
+as well as the 'usb_drv').
+
+
+The second attribute is 'report'. If its value is 'yes' the driver
+will generate a 'devices' report that contains meta information about the
+USB device it is accessing and hence the Block session it is provding, e.g.:
+
+!
+!
+!
+
+The report contains a 'device' node that includes the device's vendor name and
+the product description as well as its block size and the number of blocks.
+Although the parent node is called 'devices' the driver is only able to access
+one and the same device and the report will therefore always contain one device
+only.
+
+In addition to other attributes that can be used to configure sepecific aspects
+of the driver. The 'writeable' attribute denotes the permission of the Block
+session client to write to the USB device. Independent thereof the driver will
+query the device and will set the Block session operations accordingly. The
+'interface' specifies the USB interface the driver should use. If the device
+provides multiple SCSI devices the 'lun' attribute is used to select the right
+one.
+
+The configuration of the USB block driver cannot be changed at run-time. The
+driver is either used in a static system configuration where it is configured
+once or in case of a dynamic system configuration a new driver instance with
+a proper configuration is created on demand.
diff --git a/repos/os/src/drivers/usb_block/cbw_csw.h b/repos/os/src/drivers/usb_block/cbw_csw.h
new file mode 100644
index 000000000..45a4e32c9
--- /dev/null
+++ b/repos/os/src/drivers/usb_block/cbw_csw.h
@@ -0,0 +1,257 @@
+/*
+ * \brief USB Command Block and Status Wrapper
+ * \author Josef Soentgen
+ * \date 2016-02-08
+ */
+
+/*
+ * Copyright (C) 2016 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#ifndef _CBW_CSW_H_
+#define _CBW_CSW_H_
+
+#include
+
+namespace Usb {
+ struct Cbw;
+ struct Csw;
+}
+
+
+using Genode::uint8_t;
+using Genode::uint32_t;
+using Genode::uint64_t;
+using Genode::size_t;
+using Genode::addr_t;
+
+
+/*****************************************************
+ ** USB Command Block/Status Wrapper implementation **
+ *****************************************************/
+
+struct Usb::Cbw : Genode::Mmio
+{
+ enum { LENGTH = 31 };
+
+ enum { SIG = 0x43425355U };
+ struct Sig : Register<0x0, 32> { }; /* signature */
+ struct Tag : Register<0x4, 32> { }; /* transaction unique identifier */
+ struct Dtl : Register<0x8, 32> { }; /* data transfer length */
+ struct Flg : Register<0xc, 8> { }; /* flags */
+ struct Lun : Register<0xd, 8> { }; /* logical unit number */
+ struct Cbl : Register<0xe, 8> { }; /* command buffer length */
+
+ Cbw(addr_t addr, uint32_t t, uint32_t d,
+ uint8_t f, uint8_t l, uint8_t len) : Mmio(addr)
+ {
+ write(SIG);
+ write(t);
+ write(d);
+ write(f);
+ write(l);
+ write(len);
+ }
+
+ void dump()
+ {
+ PLOG("Sig: 0x%04x", read());
+ PLOG("Tag: %u", read());
+ PLOG("Dtl: %u", read());
+ PLOG("Flg: 0x%x", read());
+ PLOG("Lun: %u", read());
+ PLOG("Cbl: %u", read());
+ }
+};
+
+
+struct Test_unit_ready : Usb::Cbw,
+ Scsi::Test_unit_ready
+{
+ Test_unit_ready(addr_t addr, uint32_t tag, uint8_t lun)
+ :
+ Cbw(addr, tag, 0, Usb::ENDPOINT_IN, lun,
+ Scsi::Test_unit_ready::LENGTH),
+ Scsi::Test_unit_ready(addr+15)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump TEST_UNIT_READY command --");
+ Cbw::dump();
+ Scsi::Cmd_6::dump();
+ }
+};
+
+
+struct Request_sense : Usb::Cbw, Scsi::Request_sense
+{
+ Request_sense(addr_t addr, uint32_t tag, uint8_t lun)
+ :
+ Cbw(addr, tag, Scsi::Request_sense_response::LENGTH,
+ Usb::ENDPOINT_IN, lun, Scsi::Request_sense::LENGTH),
+ Scsi::Request_sense(addr+15)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump REQUEST_SENSE command --");
+ Cbw::dump();
+ Scsi::Cmd_6::dump();
+ }
+};
+
+
+struct Inquiry : Usb::Cbw, Scsi::Inquiry
+{
+ Inquiry(addr_t addr, uint32_t tag, uint8_t lun)
+ :
+ Cbw(addr, tag, Scsi::Inquiry_response::LENGTH,
+ Usb::ENDPOINT_IN, lun, Scsi::Inquiry::LENGTH),
+ Scsi::Inquiry(addr+15)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump INQUIRY command --");
+ Cbw::dump();
+ Scsi::Cmd_6::dump();
+ }
+};
+
+
+struct Read_capacity_10 : Usb::Cbw, Scsi::Read_capacity_10
+{
+ Read_capacity_10(addr_t addr, uint32_t tag, uint8_t lun)
+ :
+ Cbw(addr, tag, Scsi::Capacity_response_10::LENGTH,
+ Usb::ENDPOINT_IN, lun, Scsi::Read_capacity_10::LENGTH),
+ Scsi::Read_capacity_10(addr+15)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump READ_CAPACITY_10 command --");
+ Cbw::dump();
+ Scsi::Cmd_10::dump();
+ }
+};
+
+
+struct Read_10 : Usb::Cbw, Scsi::Read_10
+{
+ Read_10(addr_t addr, uint32_t tag, uint8_t lun,
+ uint32_t lba, uint32_t len, uint32_t block_size)
+ :
+ Cbw(addr, tag, len * block_size,
+ Usb::ENDPOINT_IN, lun, Scsi::Read_10::LENGTH),
+ Scsi::Read_10(addr+15, lba, len)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump READ_10 command --");
+ Cbw::dump();
+ Scsi::Cmd_10::dump();
+ }
+};
+
+
+struct Write_10 : Usb::Cbw, Scsi::Write_10
+{
+ Write_10(addr_t addr, uint32_t tag, uint8_t lun,
+ uint32_t lba, uint32_t len, uint32_t block_size)
+ :
+ Cbw(addr, tag, len * block_size,
+ Usb::ENDPOINT_OUT, lun, Scsi::Write_10::LENGTH),
+ Scsi::Write_10(addr+15, lba, len)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump WRITE_10 command --");
+ Cbw::dump();
+ Scsi::Cmd_10::dump();
+ }
+};
+
+
+struct Read_capacity_16 : Usb::Cbw, Scsi::Read_capacity_16
+{
+ Read_capacity_16(addr_t addr, uint32_t tag, uint8_t lun)
+ :
+ Cbw(addr, tag, Scsi::Capacity_response_16::LENGTH,
+ Usb::ENDPOINT_IN, lun, Scsi::Read_capacity_16::LENGTH),
+ Scsi::Read_capacity_16(addr+15)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump READ_CAPACITY_16 command --");
+ Cbw::dump();
+ Scsi::Cmd_16::dump();
+ }
+};
+
+
+struct Read_16 : Usb::Cbw, Scsi::Read_16
+{
+ Read_16(addr_t addr, uint32_t tag, uint8_t lun,
+ uint32_t lba, uint32_t len, uint32_t block_size)
+ :
+ Cbw(addr, tag, len * block_size,
+ Usb::ENDPOINT_IN, lun, Scsi::Read_16::LENGTH),
+ Scsi::Read_16(addr+15, lba, len)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump READ_16 command --");
+ Cbw::dump();
+ Scsi::Cmd_16::dump();
+ }
+};
+
+
+struct Write_16 : Usb::Cbw, Scsi::Write_16
+{
+ Write_16(addr_t addr, uint32_t tag, uint8_t lun,
+ uint32_t lba, uint32_t len, uint32_t block_size)
+ :
+ Cbw(addr, tag, len * block_size,
+ Usb::ENDPOINT_OUT, lun, Scsi::Write_16::LENGTH),
+ Scsi::Write_16(addr+15, lba, len)
+ { if (verbose_scsi) dump(); }
+
+ void dump()
+ {
+ PLOG("--- Dump WRITE_16 command --");
+ Cbw::dump();
+ Scsi::Cmd_16::dump();
+ }
+};
+
+
+struct Usb::Csw : Genode::Mmio
+{
+ enum { LENGTH = 13 };
+
+ enum { SIG = 0x53425355U };
+ struct Sig : Register<0x0, 32> { }; /* signature */
+ struct Tag : Register<0x4, 32> { }; /* transaction identifier */
+ struct Dr : Register<0x8, 32> { }; /* data residue */
+ enum { PASSED = 0, FAILED = 1, PHASE_ERROR = 2 };
+ struct Sts : Register<0xc, 8> { }; /* status */
+
+ Csw(addr_t addr) : Mmio(addr) { }
+
+ uint32_t sig() const { return read(); }
+ uint32_t tag() const { return read(); }
+ uint32_t dr() const { return read(); }
+ uint32_t sts() const { return read(); }
+};
+
+#endif /* _CBW_CSW_H_ */
diff --git a/repos/os/src/drivers/usb_block/main.cc b/repos/os/src/drivers/usb_block/main.cc
new file mode 100644
index 000000000..ddccdd3db
--- /dev/null
+++ b/repos/os/src/drivers/usb_block/main.cc
@@ -0,0 +1,803 @@
+/*
+ * \brief Usb session to Block session translator
+ * \author Josef Soentgen
+ * \date 2016-02-08
+ */
+
+/*
+ * Copyright (C) 2016 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+static bool verbose_scsi = false;
+
+/* local includes */
+#include
+
+
+namespace Usb {
+ using namespace Genode;
+
+ struct Block_driver;
+}
+
+
+/*********************************************************
+ ** USB Mass Storage (BBB) Block::Driver implementation **
+ *********************************************************/
+
+struct Usb::Block_driver : Usb::Completion,
+ Block::Driver
+{
+ Server::Entrypoint &ep;
+
+ Genode::Signal_context_capability announce_sigh;
+
+ /*
+ * Pending block request
+ */
+ struct Block_request
+ {
+ Block::Packet_descriptor packet;
+ Block::sector_t lba;
+ char *buffer;
+ size_t size;
+ bool read;
+ bool pending = false;
+ } req;
+
+ bool initialized = false;
+ bool device_plugged = false;
+
+ /**
+ * Handle stage change signal
+ */
+ void handle_state_change(unsigned)
+ {
+ if (!usb.plugged()) {
+ PDBG("Device unplugged");
+ device_plugged = false;
+ return;
+ }
+
+ if (initialized) {
+ PERR("Device was already initialized");
+ return;
+ }
+
+ PDBG("Device plugged");
+
+ if (initialize())
+ Genode::Signal_transmitter(announce_sigh).submit();
+ }
+
+ Server::Signal_rpc_member state_change_dispatcher = {
+ ep, *this, &Block_driver::handle_state_change };
+
+ /*
+ * Read Usb session label from the configuration
+ */
+ static char const *get_label()
+ {
+ static Genode::String<256> usb_label;
+ try {
+ Genode::config()->xml_node().attribute("label").value(&usb_label);
+ return usb_label.string();
+ } catch (...) { }
+
+ return "usb_storage";
+ }
+
+ Genode::Allocator_avl alloc;
+ Usb::Connection usb { &alloc, get_label(), 2 * (1<<20), state_change_dispatcher };
+ Usb::Device device;
+
+ Genode::Reporter reporter { "devices" };
+ bool _report_device = false;
+
+ Block::Session::Operations _block_ops;
+ Block::sector_t _block_count;
+ Genode::size_t _block_size;
+
+ bool _writeable = false;
+
+ bool force_cmd_10 = false;
+
+ uint8_t active_interface = 0;
+ uint8_t active_lun = 0;
+
+ uint32_t active_tag = 0;
+ uint32_t new_tag() { return ++active_tag % 0xffffffu; }
+
+ enum Tags {
+ INQ_TAG = 0x01, RDY_TAG = 0x02, CAP_TAG = 0x04,
+ REQ_TAG = 0x08, SS_TAG = 0x10
+ };
+ enum Endpoints { IN = 0, OUT = 1 };
+
+ /*
+ * Completion used while initializing the device
+ */
+ struct Init_completion : Usb::Completion
+ {
+ bool inquiry = false;
+ bool unit_ready = false;
+ bool read_capacity = false;
+ bool request_sense = false;
+
+ bool no_medium = false;
+ bool try_again = false;
+
+ Usb::Device &device;
+ uint8_t interface;
+
+ Block::sector_t block_count;
+ Genode::size_t block_size;
+
+ char vendor[Scsi::Inquiry_response::Vid::ITEMS+1];
+ char product[Scsi::Inquiry_response::Pid::ITEMS+1];
+
+ Init_completion(Usb::Device &device, uint8_t interface)
+ : device(device), interface(interface) { }
+
+ void complete(Packet_descriptor &p)
+ {
+ Interface iface = device.interface(interface);
+
+ if (p.type != Packet_descriptor::BULK) {
+ PERR("Can only handle BULK packets");
+ iface.release(p);
+ return;
+ }
+
+ if (!p.succeded) {
+ PERR("init complete error: packet not succeded");
+ iface.release(p);
+ return;
+ }
+
+ /* OUT transfer finished */
+ if (!p.is_read_transfer()) {
+ iface.release(p);
+ return;
+ }
+
+ int const actual_size = p.transfer.actual_size;
+ char * const data = reinterpret_cast(iface.content(p));
+
+ using namespace Scsi;
+
+ switch (actual_size) {
+ case 36: /* min INQUIRY data size */
+ case Inquiry_response::LENGTH:
+ {
+ Inquiry_response r((addr_t)data);
+ if (verbose_scsi) r.dump();
+
+ if (!r.sbc())
+ PWRN("Device does not use SCSI Block Commands and may not work");
+
+ r.get_id(vendor, sizeof(vendor));
+ r.get_id(product, sizeof(product));
+ break;
+ }
+ case Capacity_response_10::LENGTH:
+ {
+ Capacity_response_10 r((addr_t)data);
+ if (verbose_scsi) r.dump();
+
+ block_count = r.block_count();
+ block_size = r.block_size();
+ break;
+ }
+ case Capacity_response_16::LENGTH:
+ {
+ Capacity_response_16 r((addr_t)data);
+ if (verbose_scsi) r.dump();
+
+ block_count = r.block_count();
+ block_size = r.block_size();
+ break;
+ }
+ case Request_sense_response::LENGTH:
+ {
+ Request_sense_response r((addr_t)data);
+ if (verbose_scsi) r.dump();
+
+ uint8_t const asc = r.read();
+ uint8_t const asq = r.read();
+
+ enum { MEDIUM_NOT_PRESENT = 0x3a, NOT_READY_TO_READY_CHANGE = 0x28 };
+ switch (asc) {
+ case MEDIUM_NOT_PRESENT:
+ PERR("Not ready - medium not present");
+ no_medium = true;
+ break;
+ case NOT_READY_TO_READY_CHANGE: /* asq == 0x00 */
+ PWRN("Not ready - try again");
+ try_again = true;
+ break;
+ default:
+ PERR("Request_sense_response asc: 0x%02x asq: 0x%02x", asc, asq);
+ break;
+ }
+ break;
+ }
+ case Csw::LENGTH:
+ {
+ Csw csw((addr_t)data);
+
+ uint32_t const sig = csw.sig();
+ if (sig != Csw::SIG) {
+ PERR("CSW signature does not match: 0x%04x", sig);
+ break;
+ }
+
+ uint32_t const tag = csw.tag();
+ uint8_t const status = csw.sts();
+ if (status != Csw::PASSED) {
+ PERR("CSW failed: 0x%02x tag: %u", status, tag);
+ break;
+ }
+
+ inquiry |= tag & INQ_TAG;
+ unit_ready |= tag & RDY_TAG;
+ read_capacity |= tag & CAP_TAG;
+ request_sense |= tag & REQ_TAG;
+ break;
+ }
+ default: break;
+ }
+
+ iface.release(p);
+ }
+ } init { device, active_interface };
+
+ /**
+ * Send CBW
+ */
+ void cbw(void *cb, Completion *c, bool block = false)
+ {
+ enum { CBW_VALID_SIZE = Cbw::LENGTH };
+ Usb::Interface &iface = device.interface(active_interface);
+ Usb::Endpoint &ep = iface.endpoint(OUT);
+ Usb::Packet_descriptor p = iface.alloc(CBW_VALID_SIZE);
+ memcpy(iface.content(p), cb, CBW_VALID_SIZE);
+ iface.bulk_transfer(p, ep, 0, block, c);
+ }
+
+ /**
+ * Receive CSW
+ */
+ void csw(Completion *c, bool block = false)
+ {
+ enum { CSW_VALID_SIZE = Csw::LENGTH };
+ Usb::Interface &iface = device.interface(active_interface);
+ Usb::Endpoint &ep = iface.endpoint(IN);
+ Usb::Packet_descriptor p = iface.alloc(CSW_VALID_SIZE);
+ iface.bulk_transfer(p, ep, 0, block, c);
+ }
+
+ /**
+ * Receive response
+ */
+ void resp(size_t size, Completion *c, bool block = false)
+ {
+ Usb::Interface &iface = device.interface(active_interface);
+ Usb::Endpoint &ep = iface.endpoint(IN);
+ Usb::Packet_descriptor p = iface.alloc(size);
+ iface.bulk_transfer(p, ep, 0, block, c);
+ }
+
+ /**
+ * Report block device
+ */
+ void report_device(char const *vendor, char const *product,
+ Block::sector_t count, size_t size)
+ {
+ try {
+ Genode::Reporter::Xml_generator xml(reporter, [&] () {
+ xml.node("device", [&] () {
+ xml.attribute("vendor", vendor);
+ xml.attribute("product", product);
+ xml.attribute("block_count", count);
+ xml.attribute("block_size", size);
+ xml.attribute("writeable", _writeable);
+ });
+ });
+ } catch (...) { PWRN("Could not report block device"); }
+ }
+
+ /**
+ * Initialize device
+ *
+ * All USB transfers in this method are done synchronously. First we reset
+ * the device, than we query the max LUN. Afterwards we start sending CBWs.
+ *
+ * Since it might take some time for the device to get ready to use, we
+ * have to check the SCSI logical unit several times.
+ */
+ bool initialize()
+ {
+ device.update_config();
+
+ Interface &iface = device.interface(active_interface);
+ try { iface.claim(); }
+ catch (Usb::Session::Interface_already_claimed) {
+ PERR("Device already claimed");
+ return false;
+ } catch (Usb::Session::Interface_not_found) {
+ PERR("Interface not found");
+ return false;
+ }
+
+ enum {
+ ICLASS_MASS_STORAGE = 8,
+ ISUBCLASS_SCSI = 6,
+ IPROTO_BULK_ONLY = 80
+ };
+ try {
+ Alternate_interface &alt_iface = iface.alternate_interface(0);
+ iface.set_alternate_interface(alt_iface);
+
+ if (alt_iface.iclass != ICLASS_MASS_STORAGE
+ || alt_iface.isubclass != ISUBCLASS_SCSI
+ || alt_iface.iprotocol != IPROTO_BULK_ONLY) {
+ PERR("No mass storage SCSI bulk-only device");
+ return false;
+ }
+ } catch (Usb::Session::Interface_not_found) {
+ PERR("Interface not found");
+ return false;
+ }
+
+ try {
+ /* reset */
+ Usb::Packet_descriptor p = iface.alloc(0);
+ iface.control_transfer(p, 0x21, 0xff, 0, active_interface, 100);
+ if (!p.succeded) {
+ PERR("Could not reset device");
+ throw -1;
+ }
+
+ /*
+ * Let us do GetMaxLUN and simply ignore the return value because none
+ * of the devices that were tested did infact report another value than 0.
+ */
+ p = iface.alloc(1);
+ iface.control_transfer(p, 0xa1, 0xfe, 0, active_interface, 100);
+ uint8_t max_lun = *(uint8_t*)iface.content(p);
+ if (p.succeded && max_lun == 0) { max_lun = 1; }
+ iface.release(p);
+
+ /*
+ * Query device
+ */
+
+ char cbw_buffer[Cbw::LENGTH];
+
+ /*
+ * We should probably execute the SCSI REPORT_LUNS command first
+ * but we will receive LOGICAL UNIT NOT SUPPORTED if we try to
+ * access an invalid unit. The user has to specify the LUN in
+ * the configuration anyway.
+ */
+
+ /* Scsi::Opcode::INQUIRY */
+ Inquiry inq((addr_t)cbw_buffer, INQ_TAG, active_lun);
+
+ cbw(cbw_buffer, &init, true);
+ resp(Scsi::Inquiry_response::LENGTH, &init, true);
+ csw(&init, true);
+
+ if (!init.inquiry) {
+ PWRN("Inquiry_cmd failed");
+ throw -1;
+ }
+
+ /* Scsi::Opcode::TEST_UNIT_READY */
+ {
+ Timer::Connection timer;
+ /*
+ * It might take some time for devices to get ready (e.g. the ZTE Open C
+ * takes 3 retries to actually present us a medium and another try to
+ * let us use the medium.
+ */
+ enum { MAX_RETRIES = 10 };
+ int retries;
+ for (retries = 0; retries < MAX_RETRIES; retries++) {
+ Test_unit_ready unit_ready((addr_t)cbw_buffer, RDY_TAG, active_lun);
+
+ cbw(cbw_buffer, &init, true);
+ csw(&init, true);
+
+ if (!init.unit_ready) {
+ Request_sense sense((addr_t)cbw_buffer, REQ_TAG, active_lun);
+
+ cbw(cbw_buffer, &init, true);
+ resp(Scsi::Request_sense_response::LENGTH, &init, true);
+ csw(&init, true);
+ if (!init.request_sense) {
+ PWRN("Request_sense failed");
+ throw -1;
+ }
+
+ if (init.no_medium) {
+ /* do nothing for now */
+ } else if (init.try_again) {
+ init.try_again = false;
+ } else break;
+ } else break;
+
+ timer.msleep(1000);
+ }
+ if (retries == MAX_RETRIES) {
+ PWRN("Test_unit_ready_cmd failed");
+ throw -1;
+ }
+ }
+
+ /* Scsi::Opcode::READ_CAPACITY_16 */
+ Read_capacity_16 read_cap((addr_t)cbw_buffer, CAP_TAG, active_lun);
+
+ cbw(cbw_buffer, &init, true);
+ resp(Scsi::Capacity_response_16::LENGTH, &init, true);
+ csw(&init, true);
+
+ if (!init.read_capacity) {
+ /* try Scsi::Opcode::READ_CAPACITY_10 next */
+ Read_capacity_10 read_cap((addr_t)cbw_buffer, CAP_TAG, active_lun);
+
+ cbw(cbw_buffer, &init, true);
+ resp(Scsi::Capacity_response_10::LENGTH, &init, true);
+ csw(&init, true);
+
+ if (!init.read_capacity) {
+ PWRN("Read_capacity_cmd failed");
+ throw -1;
+ }
+
+ PWRN("Device does not support CDB 16-byte commands, force 10-byte commands");
+ force_cmd_10 = true;
+ }
+
+ _block_size = init.block_size;
+ _block_count = init.block_count;
+
+ initialized = true;
+ device_plugged = true;
+
+ char vendor[32];
+ char product[32];
+
+ device.manufactorer_string.to_char(vendor, sizeof(vendor));
+ device.product_string.to_char(product, sizeof(product));
+
+ PINF("Found USB device: %s (%s) block size: %zu count: %llu",
+ vendor, product, _block_size, _block_count);
+
+ if (_report_device)
+ report_device(init.vendor, init.product,
+ init.block_count, init.block_size);
+ return true;
+ } catch (int) {
+ /* handle command failures */
+ PERR("Could not initialize storage device");
+ return false;
+ } catch (...) {
+ /* handle Usb::Session failures */
+ PERR("Could not initialize storage device");
+ throw;
+ }
+ return false;
+ }
+
+ /**
+ * Execute pending request
+ *
+ * Called after the CBW has been successfully received by the device
+ * to initiate read/write transaction.
+ */
+ bool execute_pending_request()
+ {
+ Usb::Interface &iface = device.interface(active_interface);
+ Usb::Endpoint ep = iface.endpoint(req.read ? IN : OUT);
+ Usb::Packet_descriptor p = iface.alloc(req.size);
+
+ if (!req.read) memcpy(iface.content(p), req.buffer, req.size);
+
+ iface.bulk_transfer(p, ep, 0, false, this);
+
+ return true;
+ }
+
+ /**
+ * Acknowledge currently pending request
+ *
+ * After receiving the CSW ack the request at the Block session.
+ */
+ void ack_pending_request(bool success = true)
+ {
+ /*
+ * Needs to be reset bevor calling ack_packet to prevent getting a new
+ * request imediately and throwing Request_congestion() in io() again.
+ */
+ req.pending = false;
+
+ Block::Packet_descriptor p = req.packet;
+ ack_packet(p, success);
+ }
+
+ /**
+ * Handle packet completion
+ *
+ * This method is called several times while doing one transaction. First
+ * the CWB is sent, than the payload read or written. At the end, the CSW
+ * is requested.
+ */
+ void complete(Packet_descriptor &p)
+ {
+ Interface iface = device.interface(active_interface);
+
+ if (p.type != Packet_descriptor::BULK) {
+ PERR("No BULK packet");
+ iface.release(p);
+ return;
+ }
+
+ if (!p.succeded) {
+ PERR("complete error: packet not succeded");
+ if (req.pending) {
+ PERR("req.pending: tag: %u is_read: %d buffer: %p lba: %llu size: %zu",
+ active_tag, req.read, req.buffer, req.lba, req.size);
+ ack_pending_request(false);
+ }
+ iface.release(p);
+ return;
+ }
+
+ static bool request_executed = false;
+ if (!p.is_read_transfer()) {
+ /* send read/write request */
+ if (req.pending) {
+
+ /*
+ * The CBW was successfully sent to the device, now read/write the
+ * actual content.
+ */
+ if (!request_executed) {
+ request_executed = execute_pending_request();
+ } else {
+ /* the content was successfully written, get the CSW */
+ csw(this);
+ }
+ }
+
+ iface.release(p);
+ return;
+ }
+
+ int actual_size = p.transfer.actual_size;
+ if (actual_size < 0) {
+ PERR("Transfer actual size: %d", actual_size);
+ actual_size = 0;
+ }
+
+ /* the size indicates an IN I/O packet */
+ if ((uint32_t)actual_size >= _block_size) {
+ if (req.pending) {
+
+ /* the content was successfully read, get the CSW */
+ memcpy(req.buffer, iface.content(p), actual_size);
+ csw(this);
+ }
+
+ iface.release(p);
+ return;
+ }
+
+ /* when ending up here, we should have gotten an CSW packet */
+ if (actual_size != Csw::LENGTH)
+ PWRN("This is not the actual size you are looking for");
+
+ do {
+ Csw csw((addr_t)iface.content(p));
+
+ uint32_t const sig = csw.sig();
+ if (sig != Csw::SIG) {
+ PERR("CSW signature does not match: 0x%04x", sig);
+ break;
+ }
+
+ uint32_t const tag = csw.tag();
+ if (tag != active_tag) {
+ PERR("CSW tag mismatch. Got %u expected: %u", tag, active_tag);
+ break;
+ }
+
+ uint8_t const status = csw.sts();
+ if (status != Csw::PASSED) {
+ PERR("CSW failed: 0x%02x tag: %u req.read: %d req.buffer: %p "
+ "req.lba: %llu req.size: %zu", status, tag, req.read,
+ req.buffer, req.lba, req.size);
+ break;
+ }
+
+ uint32_t const dr = csw.dr();
+ if (dr) PWRN("CSW data residue: %u not considered", dr);
+
+ /* ack Block::Packet_descriptor */
+ request_executed = false;
+ ack_pending_request();
+ } while (0);
+
+ iface.release(p);
+ }
+
+ /**
+ * Parse configuration
+ */
+ void parse_config()
+ {
+ Genode::Xml_node config = Genode::config()->xml_node();
+
+ _block_ops.set_operation(Block::Packet_descriptor::READ);
+
+ _writeable = config.attribute_value("writeable", false);
+ if (_writeable)
+ _block_ops.set_operation(Block::Packet_descriptor::WRITE);
+
+ _report_device = config.attribute_value("report", false);
+
+ active_interface = config.attribute_value("interface", 0);
+ active_lun = config.attribute_value("lun", 0);
+
+ verbose_scsi = config.attribute_value("verbose_scsi", false);
+ }
+
+ /**
+ * Constructor
+ *
+ * \param alloc allocator used by Usb::Connection
+ * \param ep Server::Endpoint
+ * \param sigh signal context used for annoucing Block service
+ */
+ Block_driver(Genode::Allocator &alloc, Server::Entrypoint &ep,
+ Genode::Signal_context_capability sigh)
+ :
+ ep(ep), announce_sigh(sigh), alloc(Genode::env()->heap()),
+ device(Genode::env()->heap(), usb, ep)
+ {
+ parse_config();
+ reporter.enabled(true);
+
+ /* USB device gets initialized by handle_state_change() */
+ }
+
+ /**
+ * Send CBW
+ */
+ void send_cbw(Block::sector_t lba, size_t len, bool read)
+ {
+ uint32_t const t = new_tag();
+
+ char cb[Cbw::LENGTH];
+ if (read) {
+ if (!force_cmd_10) Read_16 r((addr_t)cb, t, active_lun, lba, len, _block_size);
+ else Read_10 r((addr_t)cb, t, active_lun, lba, len, _block_size);
+ } else {
+ if (!force_cmd_10) Write_16 w((addr_t)cb, t, active_lun, lba, len, _block_size);
+ else Write_10 w((addr_t)cb, t, active_lun, lba, len, _block_size);
+ }
+
+ cbw(cb, this);
+ }
+
+ /**
+ * Perform IO/ request
+ *
+ * \param read set to true when reading, false when writting
+ * \param lba address of the starting block
+ * \param buffer source/destination buffer
+ * \param p Block::Packet_descriptor
+ */
+ void io(bool read, Block::sector_t lba, size_t count,
+ char *buffer, Block::Packet_descriptor &p)
+ {
+ if (!device_plugged) throw Io_error();
+ if (lba+count > _block_count) throw Io_error();
+ if (req.pending) throw Request_congestion();
+
+ req.pending = true;
+ req.packet = p;
+ req.lba = lba;
+ req.size = count * _block_size;
+ req.buffer = buffer;
+ req.read = read;
+
+ send_cbw(lba, count, read);
+ }
+
+ /*******************************
+ ** Block::Driver interface **
+ *******************************/
+
+ size_t block_size() override { return _block_size; }
+ Block::sector_t block_count() override { return _block_count; }
+ Block::Session::Operations ops() override { return _block_ops; }
+
+ void read(Block::sector_t lba, size_t count,
+ char *buffer, Block::Packet_descriptor &p) override {
+ io(true, lba, count, buffer, p); }
+
+ void write(Block::sector_t lba, size_t count,
+ char const *buffer, Block::Packet_descriptor &p) override {
+ io(false, lba, count, const_cast(buffer), p); }
+
+ void sync() override { /* maybe implement SYNCHRONIZE_CACHE_10/16? */ }
+};
+
+
+struct Main
+{
+ Server::Entrypoint &ep;
+
+ void announce(unsigned)
+ {
+ Genode::env()->parent()->announce(ep.manage(root));
+ }
+
+ Server::Signal_rpc_member announce_dispatcher {
+ ep, *this, &Main::announce };
+
+ struct Factory : Block::Driver_factory
+ {
+ Genode::Allocator &alloc;
+ Server::Entrypoint &ep;
+ Genode::Signal_context_capability sigh;
+
+ Usb::Block_driver *driver = nullptr;
+
+ Factory(Genode::Allocator &alloc, Server::Entrypoint &ep,
+ Genode::Signal_context_capability sigh)
+ : alloc(alloc), ep(ep), sigh(sigh)
+ {
+ driver = new (Genode::env()->heap()) Usb::Block_driver(alloc, ep, sigh);
+ }
+
+ Block::Driver *create() { return driver; }
+
+ void destroy(Block::Driver *driver) { }
+ };
+
+ Factory factory { *Genode::env()->heap(), ep, announce_dispatcher };
+ Block::Root root;
+
+ Main(Server::Entrypoint &ep)
+ : ep(ep), root(ep, Genode::env()->heap(), factory) { }
+};
+
+
+/************
+ ** Server **
+ ************/
+
+namespace Server {
+ char const *name() { return "usb_block_ep"; }
+ size_t stack_size() { return 2*1024*sizeof(long); }
+ void construct(Entrypoint &ep) { static Main main(ep); }
+}
diff --git a/repos/os/src/drivers/usb_block/scsi.h b/repos/os/src/drivers/usb_block/scsi.h
new file mode 100644
index 000000000..12b26dcab
--- /dev/null
+++ b/repos/os/src/drivers/usb_block/scsi.h
@@ -0,0 +1,427 @@
+/*
+ * \brief SCSI Block Commands
+ * \author Josef Soentgen
+ * \date 2016-02-08
+ */
+
+/*
+ * Copyright (C) 2016 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#ifndef _SCSI_H_
+#define _SCSI_H_
+
+#include
+#include
+#include
+
+namespace Scsi {
+
+ using namespace Genode;
+
+ uint16_t be16(uint16_t val);
+ uint32_t be32(uint32_t val);
+ uint64_t be64(uint64_t val);
+
+ /******************
+ * SCSI commands **
+ ******************/
+
+ enum Opcode {
+ TEST_UNIT_READY = 0x00,
+ REQUEST_SENSE = 0x03,
+ INQUIRY = 0x12,
+ START_STOP = 0x1B,
+ READ_CAPACITY_10 = 0x25,
+ READ_10 = 0x28,
+ WRITE_10 = 0x2a,
+ READ_16 = 0x88,
+ WRITE_16 = 0x8a,
+ READ_CAPACITY_16 = 0x9e,
+ };
+
+ struct Inquiry_response;
+ struct Request_sense_response;
+ struct Capacity_response_10;
+ struct Capacity_response_16;
+
+ struct Cmd_6;
+ struct Test_unit_ready;
+ struct Request_sense;
+ struct Inquiry;
+ struct Start_stop;
+
+ struct Cmd_10;
+ struct Read_capacity_10;
+ struct Io_10;
+ struct Read_10;
+ struct Write_10;
+
+ struct Cmd_16;
+ struct Read_capacity_16;
+ struct Io_16;
+ struct Read_16;
+ struct Write_16;
+}
+
+
+/*******************
+** Endian helper **
+*******************/
+
+Genode::uint16_t Scsi::be16(Genode::uint16_t val)
+{
+ Genode::uint8_t *p = reinterpret_cast(&val);
+ return (p[1]<<0)|(p[0]<<8);
+}
+
+Genode::uint32_t Scsi::be32(Genode::uint32_t val)
+{
+ Genode::uint8_t *p = reinterpret_cast(&val);
+ return (p[3]<<0)|(p[2]<<8)|(p[1]<<16)|(p[0]<<24);
+}
+
+Genode::uint64_t Scsi::be64(Genode::uint64_t val)
+{
+ Genode::uint8_t *p = reinterpret_cast(&val);
+ return ((((Genode::uint64_t)(p[3]<<0)|(p[2]<<8)|(p[1]<<16)|(p[0]<<24))<<32)|
+ (((Genode::uint32_t)(p[7]<<0)|(p[6]<<8)|(p[5]<<16)|(p[4]<<24))));
+}
+
+
+/***************************
+ * SCSI command responses **
+ ***************************/
+
+struct Scsi::Inquiry_response : Genode::Mmio
+{
+ enum { LENGTH = 36 /* default */ + 20 /* drive serial number and vendor unique */};
+
+ struct Dt : Register<0x0, 8> { }; /* device type */
+ struct Rm : Register<0x1, 8> { struct Rmb : Bitfield<7, 1> { }; }; /* removable media */
+ struct Ver : Register<0x2, 8> { }; /* version */
+ struct Rdf : Register<0x3, 8> { }; /* response data format */
+ struct Al : Register<0x4, 8> { }; /* additional ength */
+ struct Flg : Register<0x7, 8> { }; /* flags */
+ struct Vid : Register_array<0x8, 8, 8, 8> { }; /* vendor identification */
+ struct Pid : Register_array<0x10, 8, 16, 8> { }; /* product identification */
+ struct Rev : Register_array<0x20, 8, 4, 8> { }; /* product revision level */
+
+ Inquiry_response(addr_t addr) : Mmio(addr) { }
+
+ bool sbc() const { return read() == 0x00; }
+ bool removable() const { return read(); }
+
+ template
+ bool get_id(char *dst, size_t len)
+ {
+ if (len < ID::ITEMS+1) return false;
+ for (uint32_t i = 0; i < ID::ITEMS; i++) dst[i] = read(i);
+ dst[ID::ITEMS] = 0;
+ return true;
+ }
+
+ void dump()
+ {
+ PLOG("--- Dump INQUIRY data ---");
+ PLOG("Dt: 0x%02x", read());
+ PLOG("Rm::Rmb: %u ", read());
+ PLOG("Ver: 0x%02x", read());
+ PLOG("Rdf: 0x%02x", read());
+ PLOG("Al: %u", read());
+ PLOG("Flg: 0x%02x", read());
+ }
+};
+
+
+struct Scsi::Request_sense_response : Genode::Mmio
+{
+ enum { LENGTH = 18 };
+
+ struct Rc : Register<0x0, 8>
+ {
+ struct V : Bitfield<6, 1> { }; /* valid bit */
+ struct Ec : Bitfield<0, 7> { }; /* error code */
+ }; /* response code */
+ struct Flg : Register<0x2, 8>
+ {
+ struct Sk : Bitfield<0, 4> { }; /* sense key */
+ }; /* flags */
+ struct Inf : Register<0x3, 32> { }; /* information BE */
+ struct Asl : Register<0x7, 8> { }; /* additional sense length */
+ struct Csi : Register<0x8, 32> { }; /* command specific information BE */
+ struct Asc : Register<0xc, 8> { }; /* additional sense code */
+ struct Asq : Register<0xd, 8> { }; /* additional sense code qualifier */
+ struct Fru : Register<0xe, 8> { }; /* field replaceable unit code */
+ struct Sks : Register<0xf, 32> { }; /* sense key specific (3 byte) */
+
+ Request_sense_response(addr_t addr) : Mmio(addr) { }
+
+ void dump()
+ {
+ PLOG("--- Dump REQUEST_SENSE data ---");
+ PLOG("Rc::V: %u", read());
+ PLOG("Rc::Ec: 0x%02x", read());
+ PLOG("Flg::Sk: 0x%02x", read());
+ PLOG("Asc: 0x%02x", read());
+ PLOG("Asq: 0x%02x", read());
+ }
+};
+
+
+struct Scsi::Capacity_response_10 : Genode::Mmio
+{
+ enum { LENGTH = 8 };
+
+ struct Bc : Register<0x0, 32> { };
+ struct Bs : Register<0x4, 32> { };
+
+ Capacity_response_10(addr_t addr) : Mmio(addr) { }
+
+ uint32_t block_count() const { return be32(read()); }
+ uint32_t block_size() const { return be32(read()); }
+
+ void dump()
+ {
+ PLOG("--- Dump READ_CAPACITY_10 data ---");
+ PLOG("Bc: 0x%04x", block_count());
+ PLOG("Bs: 0x%04x", block_size());
+ }
+};
+
+
+struct Scsi::Capacity_response_16 : Genode::Mmio
+{
+ enum { LENGTH = 32 };
+
+ struct Bc : Register<0x0, 64> { };
+ struct Bs : Register<0x8, 32> { };
+
+ Capacity_response_16(addr_t addr) : Mmio(addr) { }
+
+ uint64_t block_count() const { return be64(read()); }
+ uint32_t block_size() const { return be32(read()); }
+
+ void dump()
+ {
+ PLOG("--- Dump READ_CAPACITY_16 data ---");
+ PLOG("Bc: 0x%08llx", block_count());
+ PLOG("Bs: 0x%04x", block_size());
+ }
+};
+
+
+/*************************
+ ** CBD 6 byte commands **
+ *************************/
+
+struct Scsi::Cmd_6 : Genode::Mmio
+{
+ enum { LENGTH = 6 };
+ struct Op : Register<0x0, 8> { }; /* SCSI command */
+ struct Lba : Register<0x2, 16> { }; /* logical block address */
+ struct Len : Register<0x4, 8> { }; /* transfer length */
+ struct Ctl : Register<0x5, 8> { }; /* controll */
+
+ Cmd_6(addr_t addr) : Mmio(addr) { memset((void*)addr, 0, LENGTH); }
+
+ void dump()
+ {
+ PLOG("Op: 0x%02x", read());
+ PLOG("Lba: 0x%02x", be16(read()));
+ PLOG("Len: %u", read());
+ PLOG("Ctl: 0x%02x", read());
+ }
+};
+
+
+struct Scsi::Test_unit_ready : Cmd_6
+{
+ Test_unit_ready(addr_t addr) : Cmd_6(addr)
+ {
+ write(Opcode::TEST_UNIT_READY);
+ }
+};
+
+
+struct Scsi::Request_sense : Cmd_6
+{
+ Request_sense(addr_t addr) : Cmd_6(addr)
+ {
+ write(Opcode::REQUEST_SENSE);
+ write(Request_sense_response::LENGTH);
+ }
+};
+
+
+struct Scsi::Inquiry : Cmd_6
+{
+ Inquiry(addr_t addr) : Cmd_6(addr)
+ {
+ write(Opcode::INQUIRY);
+ write(Inquiry_response::LENGTH);
+ }
+};
+
+
+/* not in use for now but might come in handy in the future */
+struct Scsi::Start_stop : Genode::Mmio
+{
+ enum { LENGTH = 6 };
+ struct Op : Register<0x0, 8> { }; /* SCSI command */
+ struct I : Register<0x1, 8> {
+ struct Immed : Bitfield<0, 1> { }; }; /* immediate */
+ struct Flg : Register<0x4, 8>
+ {
+ struct Pwc : Bitfield<4, 4> { }; /* power condition */
+ struct Loej : Bitfield<1, 1> { }; /* load eject */
+ struct St : Bitfield<0, 1> { }; /* start */
+ }; /* flags */
+
+ Start_stop(addr_t addr) : Mmio(addr)
+ {
+ memset((void*)addr, 0, LENGTH);
+
+ write(Opcode::START_STOP);
+ write(1);
+ write(0);
+ write(1);
+ write(1);
+ }
+
+ void dump()
+ {
+ PLOG("Op: 0x%02x", read());
+ PLOG("I::Immed: %u", read());
+ PLOG("Flg::Pwc: 0x%02x", read());
+ PLOG("Flg::Loej: %u", read());
+ PLOG("Flg::St: %u", read());
+ }
+};
+
+
+/**************************
+ ** CBD 10 byte commands **
+ **************************/
+
+struct Scsi::Cmd_10 : Genode::Mmio
+{
+ enum { LENGTH = 10 };
+ struct Op : Register<0x0, 8> { }; /* SCSI command */
+ struct Lba : Register<0x2, 32> { }; /* logical block address */
+ struct Len : Register<0x7, 16> { }; /* transfer length */
+ struct Ctl : Register<0x9, 8> { }; /* controll */
+
+ Cmd_10(addr_t addr) : Mmio(addr) { memset((void*)addr, 0, LENGTH); }
+
+ void dump()
+ {
+ PLOG("Op: 0x%0x", read());
+ PLOG("Lba: 0x%0x", be32(read()));
+ PLOG("Len: %u", be16(read()));
+ PLOG("Ctl: 0x%0x", read());
+ }
+};
+
+
+struct Scsi::Read_capacity_10 : Cmd_10
+{
+ Read_capacity_10(addr_t addr) : Cmd_10(addr)
+ {
+ write(Opcode::READ_CAPACITY_10);
+ }
+};
+
+
+struct Scsi::Io_10 : Cmd_10
+{
+ Io_10(addr_t addr, uint32_t lba, uint16_t len) : Cmd_10(addr)
+ {
+ write(be32(lba));
+ write(be16(len));
+ }
+};
+
+
+struct Scsi::Read_10 : Io_10
+{
+ Read_10(addr_t addr, uint32_t lba, uint16_t len) : Io_10(addr, lba, len)
+ {
+ write(Opcode::READ_10);
+ }
+};
+
+
+struct Scsi::Write_10 : Io_10
+{
+ Write_10(addr_t addr, uint32_t lba, uint16_t len) : Io_10(addr, lba, len)
+ {
+ write(Opcode::WRITE_10);
+ }
+};
+
+
+/***********************************
+ ** CBD 16 long LBA byte commands **
+ ***********************************/
+
+struct Scsi::Cmd_16 : Genode::Mmio
+{
+ enum { LENGTH = 16 };
+ struct Op : Register<0x0, 8> { }; /* SCSI command */
+ struct Lba : Register<0x2, 64> { }; /* logical block address */
+ struct Len : Register<0xa, 32> { }; /* transfer length */
+ struct Ctl : Register<0xf, 8> { }; /* controll */
+
+ Cmd_16(addr_t addr) : Mmio(addr) { memset((void*)addr, 0, LENGTH); }
+
+ void dump()
+ {
+ PLOG("Op: 0x%0x", read());
+ PLOG("Lba: 0x%0llx", be64(read()));
+ PLOG("Len: %u", be32(read()));
+ PLOG("Ctl: 0x%0x", read());
+ }
+};
+
+
+struct Scsi::Read_capacity_16 : Cmd_16
+{
+ Read_capacity_16(addr_t addr) : Cmd_16(addr)
+ {
+ write(Opcode::READ_CAPACITY_16);
+ }
+};
+
+
+struct Scsi::Io_16 : Cmd_16
+{
+ Io_16(addr_t addr, uint64_t lba, uint32_t len) : Cmd_16(addr)
+ {
+ write(be64(lba));
+ write(be32(len));
+ }
+};
+
+
+struct Scsi::Read_16 : Io_16
+{
+ Read_16(addr_t addr, uint64_t lba, uint32_t len) : Io_16(addr, lba, len)
+ {
+ write(Opcode::READ_16);
+ }
+};
+
+
+struct Scsi::Write_16 : Io_16
+{
+ Write_16(addr_t addr, uint64_t lba, uint32_t len) : Io_16(addr, lba, len)
+ {
+ write(Opcode::WRITE_16);
+ }
+};
+
+#endif /* _SCSI_H_ */
diff --git a/repos/os/src/drivers/usb_block/target.mk b/repos/os/src/drivers/usb_block/target.mk
new file mode 100644
index 000000000..65e674716
--- /dev/null
+++ b/repos/os/src/drivers/usb_block/target.mk
@@ -0,0 +1,4 @@
+TARGET = usb_block_drv
+SRC_CC = main.cc
+INC_DIR = $(PRG_DIR)
+LIBS = base config server