diff --git a/repos/os/src/drivers/usb_block/main.cc b/repos/os/src/drivers/usb_block/main.cc
index cbccece76..eb068f5d3 100644
--- a/repos/os/src/drivers/usb_block/main.cc
+++ b/repos/os/src/drivers/usb_block/main.cc
@@ -1,11 +1,12 @@
/*
* \brief Usb session to Block session translator
* \author Josef Soentgen
+ * \author Sebastian Sumpf
* \date 2016-02-08
*/
/*
- * Copyright (C) 2016-2017 Genode Labs GmbH
+ * Copyright (C) 2016-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@@ -13,14 +14,13 @@
/* Genode includes */
#include
+#include
#include
#include
#include
#include
#include
-#include
-#include
-#include
+#include
#include
#include
#include
@@ -34,8 +34,11 @@ static bool verbose_scsi = false;
namespace Usb {
using namespace Genode;
+ using namespace Block;
+ using Response = Block::Request_stream::Response;
struct Block_driver;
+ struct Block_session_component;
struct Main;
}
@@ -44,8 +47,7 @@ namespace Usb {
** USB Mass Storage (BBB) Block::Driver implementation **
*********************************************************/
-struct Usb::Block_driver : Usb::Completion,
- Block::Driver
+struct Usb::Block_driver : Usb::Completion
{
Env &env;
Entrypoint &ep;
@@ -57,13 +59,34 @@ struct Usb::Block_driver : Usb::Completion,
*/
struct Block_request
{
- Block::Packet_descriptor packet { };
- Block::sector_t lba { 0 };
- char *buffer { nullptr };
- size_t size { 0 };
- bool read { false };
- bool pending { false };
- } req { };
+ Request block_request;
+ addr_t address { 0 };
+ size_t size { 0 };
+ bool completed { false };
+
+ Block_request(Request &request, Request_stream::Payload const &payload)
+ : block_request(request)
+ {
+ payload.with_content(request, [&](void *addr, size_t sz) {
+ address = addr_t(addr);
+ size = sz;
+ });
+ }
+
+ bool read() const
+ {
+ return block_request.operation.type == Operation::Type::READ;
+ }
+
+ bool write() const
+ {
+ return block_request.operation.type == Operation::Type::WRITE;
+ }
+ };
+
+ Constructible request { };
+
+ bool request_pending() const { return request.constructed(); }
bool initialized = false;
bool device_plugged = false;
@@ -74,21 +97,21 @@ struct Usb::Block_driver : Usb::Completion,
void handle_state_change()
{
if (!usb.plugged()) {
- Genode::log("Device unplugged");
+ log("Device unplugged");
device_plugged = false;
return;
}
if (initialized) {
- Genode::error("Device was already initialized");
+ error("Device was already initialized");
return;
}
- Genode::log("Device plugged");
+ log("Device plugged");
if (!initialize()) {
env.parent().exit(-1);
- Genode::sleep_forever();
+ sleep_forever();
return;
}
@@ -107,23 +130,26 @@ struct Usb::Block_driver : Usb::Completion,
/*
* Read Usb session label from the configuration
*/
- static char const *get_label(Xml_node node)
+ template
+ static Genode::String get_label(Xml_node node)
{
- static Genode::String<256> usb_label;
+ Genode::String usb_label { "usb_storage" };
try {
node.attribute("label").value(usb_label);
return usb_label.string();
} catch (...) { }
- return "usb_storage";
+ return usb_label;
}
/*
* USB session
*/
- Allocator_avl alloc;
- Usb::Connection usb { env, &alloc, get_label(config.xml()), 2 * (1<<20), state_change_dispatcher };
- Usb::Device device;
+ Allocator_avl alloc;
+ Usb::Connection usb { env, &alloc,
+ get_label<128>(config.xml()).string(), 2 * (1<<20), state_change_dispatcher };
+ Usb::Device device;
+ Signal_handler &wake_up_handler;
/*
* Reporter
@@ -188,13 +214,13 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Interface iface = device.interface(interface);
if (p.type != Packet_descriptor::BULK) {
- Genode::error("Can only handle BULK packets");
+ error("Can only handle BULK packets");
iface.release(p);
return;
}
if (!p.succeded) {
- Genode::error("init complete error: packet not succeeded");
+ error("init complete error: packet not succeeded");
iface.release(p);
return;
}
@@ -217,7 +243,7 @@ struct Usb::Block_driver : Usb::Completion,
if (verbose_scsi) r.dump();
if (!r.sbc()) {
- Genode::warning("Device does not use SCSI Block Commands and may not work");
+ warning("Device does not use SCSI Block Commands and may not work");
}
r.get_id(vendor, sizeof(vendor));
@@ -265,7 +291,7 @@ struct Usb::Block_driver : Usb::Completion,
case NOT_READY_TO_READY_CHANGE: /* asq == 0x00 */
case POWER_ON_OR_RESET_OCCURRED: /* asq == 0x00 */
- Genode::warning("Not ready - try again");
+ warning("Not ready - try again");
try_again = true;
break;
@@ -294,7 +320,7 @@ struct Usb::Block_driver : Usb::Completion,
uint32_t const sig = csw.sig();
if (sig != Csw::SIG) {
- Genode::error("CSW signature does not match: ",
+ error("CSW signature does not match: ",
Hex(sig, Hex::PREFIX, Hex::PAD));
break;
}
@@ -302,7 +328,7 @@ struct Usb::Block_driver : Usb::Completion,
uint32_t const tag = csw.tag();
uint8_t const status = csw.sts();
if (status != Csw::PASSED) {
- Genode::error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
+ error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
" tag: ", tag);
break;
}
@@ -363,7 +389,7 @@ struct Usb::Block_driver : Usb::Completion,
Block::sector_t count, size_t size)
{
try {
- Genode::Reporter::Xml_generator xml(reporter, [&] () {
+ Reporter::Xml_generator xml(reporter, [&] () {
xml.node("device", [&] () {
xml.attribute("vendor", vendor);
xml.attribute("product", product);
@@ -372,7 +398,7 @@ struct Usb::Block_driver : Usb::Completion,
xml.attribute("writeable", _writeable);
});
});
- } catch (...) { Genode::warning("Could not report block device"); }
+ } catch (...) { warning("Could not report block device"); }
}
/**
@@ -392,10 +418,10 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Interface &iface = device.interface(active_interface);
try { iface.claim(); }
catch (Usb::Session::Interface_already_claimed) {
- Genode::error("Device already claimed");
+ error("Device already claimed");
return false;
} catch (Usb::Session::Interface_not_found) {
- Genode::error("Interface not found");
+ error("Interface not found");
return false;
}
@@ -410,7 +436,7 @@ struct Usb::Block_driver : Usb::Completion,
if (alt_iface.iclass != ICLASS_MASS_STORAGE
|| alt_iface.isubclass != ISUBCLASS_SCSI
|| alt_iface.iprotocol != IPROTO_BULK_ONLY) {
- Genode::error("No mass storage SCSI bulk-only device");
+ error("No mass storage SCSI bulk-only device");
return false;
}
@@ -437,7 +463,7 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Packet_descriptor p = iface.alloc(0);
iface.control_transfer(p, 0x21, 0xff, 0, active_interface, 100);
if (!p.succeded) {
- Genode::error("Could not reset device");
+ error("Could not reset device");
iface.release(p);
throw -1;
}
@@ -475,7 +501,7 @@ struct Usb::Block_driver : Usb::Completion,
csw(init, true);
if (!init.inquiry) {
- Genode::warning("Inquiry_cmd failed");
+ warning("Inquiry_cmd failed");
throw -1;
}
@@ -502,7 +528,7 @@ struct Usb::Block_driver : Usb::Completion,
resp(Scsi::Request_sense_response::LENGTH, init, true);
csw(init, true);
if (!init.request_sense) {
- Genode::warning("Request_sense failed");
+ warning("Request_sense failed");
throw -1;
}
@@ -524,7 +550,7 @@ struct Usb::Block_driver : Usb::Completion,
timer.msleep(1000);
}
if (retries == MAX_RETRIES) {
- Genode::warning("Test_unit_ready_cmd failed");
+ warning("Test_unit_ready_cmd failed");
throw -1;
}
}
@@ -547,7 +573,7 @@ struct Usb::Block_driver : Usb::Completion,
csw(init, true);
if (!init.read_capacity) {
- Genode::warning("Read_capacity_cmd failed");
+ warning("Read_capacity_cmd failed");
throw -1;
}
@@ -563,7 +589,7 @@ struct Usb::Block_driver : Usb::Completion,
csw(init, true);
if (!init.read_capacity) {
- Genode::warning("Read_capacity_cmd failed");
+ warning("Read_capacity_cmd failed");
throw -1;
}
@@ -582,7 +608,7 @@ struct Usb::Block_driver : Usb::Completion,
device.manufactorer_string.to_char(vendor, sizeof(vendor));
device.product_string.to_char(product, sizeof(product));
- Genode::log("Found USB device: ", (char const*)vendor, " (",
+ log("Found USB device: ", (char const*)vendor, " (",
(char const*)product, ") block size: ", _block_size,
" count: ", _block_count);
@@ -592,10 +618,10 @@ struct Usb::Block_driver : Usb::Completion,
return true;
} catch (int) {
/* handle command failures */
- Genode::error("Could not initialize storage device");
+ error("Could not initialize storage device");
} catch (...) {
/* handle Usb::Session failures */
- Genode::error("Could not initialize storage device");
+ error("Could not initialize storage device");
}
return false;
}
@@ -609,31 +635,23 @@ struct Usb::Block_driver : Usb::Completion,
bool execute_pending_request()
{
Usb::Interface &iface = device.interface(active_interface);
- Usb::Endpoint ep = iface.endpoint(req.read ? ep_in : ep_out);
- Usb::Packet_descriptor p = iface.alloc(req.size);
+ Usb::Endpoint ep = iface.endpoint(request->read() ? ep_in : ep_out);
+ Usb::Packet_descriptor p = iface.alloc(request->size);
- if (!req.read) memcpy(iface.content(p), req.buffer, req.size);
+ if (request->write())
+ memcpy(iface.content(p), (void *)request->address, request->size);
iface.bulk_transfer(p, ep, 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)
+ void wakeup_client(bool success)
{
- /*
- * 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;
+ request->block_request.success = success;
+ request->completed = true;
- Block::Packet_descriptor p = req.packet;
- ack_packet(p, success);
+ wake_up_handler.dispatch(0);
}
/**
@@ -648,18 +666,19 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Interface iface = device.interface(active_interface);
if (p.type != Packet_descriptor::BULK) {
- Genode::error("No BULK packet");
+ error("No BULK packet");
iface.release(p);
return;
}
if (!p.succeded) {
- Genode::error("complete error: packet not succeded");
- if (req.pending) {
- Genode::error("request pending: tag: ", active_tag, " read: ",
- (int)req.read, " buffer: ", (void *)req.buffer, " lba: ",
- req.lba, " size: ", req.size);
- ack_pending_request(false);
+ error("complete error: packet not succeded");
+ if (request_pending()) {
+ error("request pending: tag: ", active_tag, " read: ",
+ request->read(), " buffer: ", Hex(request->address),
+ " lba: ", request->block_request.operation.block_number,
+ " size: ", request->size);
+ wakeup_client(false);
}
iface.release(p);
return;
@@ -668,7 +687,7 @@ struct Usb::Block_driver : Usb::Completion,
static bool request_executed = false;
if (!p.read_transfer()) {
/* send read/write request */
- if (req.pending) {
+ if (request_pending()) {
/*
* The CBW was successfully sent to the device, now read/write the
@@ -688,16 +707,16 @@ struct Usb::Block_driver : Usb::Completion,
int actual_size = p.transfer.actual_size;
if (actual_size < 0) {
- Genode::error("Transfer actual size: ", actual_size);
+ error("Transfer actual size: ", actual_size);
actual_size = 0;
}
/* the size indicates an IN I/O packet */
if ((uint32_t)actual_size >= _block_size) {
- if (req.pending) {
+ if (request_pending()) {
/* the content was successfully read, get the CSW */
- memcpy(req.buffer, iface.content(p), actual_size);
+ memcpy((void *)request->address, iface.content(p), actual_size);
csw(*this);
}
@@ -707,41 +726,42 @@ struct Usb::Block_driver : Usb::Completion,
/* when ending up here, we should have gotten an CSW packet */
if (actual_size != Csw::LENGTH)
- Genode::warning("This is not the actual size you are looking for");
+ warning("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) {
- Genode::error("CSW signature does not match: ",
- Hex(sig, Hex::PREFIX, Hex::PAD));
+ error("CSW signature does not match: ",
+ Hex(sig, Hex::PREFIX, Hex::PAD));
break;
}
uint32_t const tag = csw.tag();
if (tag != active_tag) {
- Genode::error("CSW tag mismatch. Got ", tag, " expected: ",
- active_tag);
+ error("CSW tag mismatch. Got ", tag, " expected: ",
+ active_tag);
break;
}
uint8_t const status = csw.sts();
if (status != Csw::PASSED) {
- Genode::error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
- " read: ", (int)req.read, " buffer: ", (void *)req.buffer,
- " lba: ", req.lba, " size: ", req.size);
+ error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
+ " read: ", request->read(), " buffer: ", (void *)request->address,
+ " lba: ", request->block_request.operation.block_number,
+ " size: ", request->size);
break;
}
uint32_t const dr = csw.dr();
if (dr) {
- Genode::warning("CSW data residue: ", dr, " not considered");
+ warning("CSW data residue: ", dr, " not considered");
}
/* ack Block::Packet_descriptor */
request_executed = false;
- ack_pending_request();
+ wakeup_client(true);
} while (0);
iface.release(p);
@@ -767,12 +787,12 @@ struct Usb::Block_driver : Usb::Completion,
* \param ep Server::Endpoint
* \param sigh signal context used for annoucing Block service
*/
- Block_driver(Env &env, Genode::Allocator &alloc,
- Genode::Signal_context_capability sigh)
+ Block_driver(Env &env, Allocator &alloc,
+ Genode::Signal_context_capability sigh,
+ Signal_handler &wake_up_handler)
:
- Block::Driver(env.ram()),
env(env), ep(env.ep()), announce_sigh(sigh), alloc(&alloc),
- device(&alloc, usb, ep)
+ device(&alloc, usb, ep), wake_up_handler(wake_up_handler)
{
parse_config(config.xml());
reporter.enabled(true);
@@ -789,111 +809,196 @@ struct Usb::Block_driver : Usb::Completion,
/**
* Send CBW
*/
- void send_cbw(Block::sector_t lba, size_t len, bool read)
+ void send_cbw(block_number_t lba, block_count_t count, bool read)
{
uint32_t const t = new_tag();
char cb[Cbw::LENGTH];
if (read) {
- if (force_cmd_16) 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);
+ if (force_cmd_16) Read_16 r((addr_t)cb, t, active_lun, lba, count, _block_size);
+ else Read_10 r((addr_t)cb, t, active_lun, lba, count, _block_size);
} else {
- if (force_cmd_16) 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);
+ if (force_cmd_16) Write_16 w((addr_t)cb, t, active_lun, lba, count, _block_size);
+ else Write_10 w((addr_t)cb, t, active_lun, lba, count, _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();
+ /*******************************************
+ ** interface to Block_session_component **
+ *******************************************/
- 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 **
- *******************************/
-
- Block::Session::Info info() const override
+ Block::Session::Info info() const
{
return { .block_size = _block_size,
.block_count = _block_count,
- .align_log2 = Genode::log2(_block_size),
+ .align_log2 = log2(_block_size),
.writeable = _writeable };
}
- void read(Block::sector_t lba, size_t count,
- char *buffer, Block::Packet_descriptor &p) override {
- io(true, lba, count, buffer, p); }
+ Response submit(Request &block_request,
+ Request_stream::Payload const &payload)
+ {
+ if (device_plugged == false)
+ return Response::REJECTED;
- 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); }
+ Operation const &op = block_request.operation;
- void sync() override { /* maybe implement SYNCHRONIZE_CACHE_10/16? */ }
+ /* read only */
+ if (info().writeable == false &&
+ op.type == Operation::Type::WRITE)
+ return Response::REJECTED;
+
+ /* range check */
+ block_number_t const last = op.block_number + op.count;
+ if (last >= info().block_count)
+ return Response::REJECTED;
+
+ /* check if request is pending */
+ if (request_pending())
+ return Response::RETRY;
+
+ request.construct(block_request, payload);
+
+ /* execute */
+ send_cbw(op.block_number, op.count, request->read());
+
+ return Response::ACCEPTED;
+ }
+
+ template
+ void with_completed(FUNC const &fn)
+ {
+ if (request_pending() == false || request->completed == false) return;
+
+ fn(request->block_request);
+
+ request.destruct();
+ }
};
-struct Usb::Main
+struct Usb::Block_session_component : Rpc_object,
+ Block::Request_stream
+{
+ Env &_env;
+
+ Block_session_component(Env &env, Dataspace_capability ds,
+ Signal_context_capability sigh,
+ Block::Session::Info info)
+ :
+ Request_stream(env.rm(), ds, env.ep(), sigh, info),
+ _env(env)
+ {
+ _env.ep().manage(*this);
+ }
+
+ ~Block_session_component() { _env.ep().dissolve(*this); }
+
+ Info info() const override { return Request_stream::info(); }
+
+ Capability tx_cap() override { return Request_stream::tx_cap(); }
+};
+
+
+
+struct Usb::Main : Rpc_object>
{
Env &env;
Heap heap { env.ram(), env.rm() };
- void announce()
- {
- env.parent().announce(env.ep().manage(root));
- }
+ Constructible block_ds { };
+ Constructible block_session { };
Signal_handler announce_dispatcher {
env.ep(), *this, &Usb::Main::announce };
- struct Factory : Block::Driver_factory
+ Io_signal_handler request_handler {
+ env.ep(), *this, &Main::handle_requests };
+
+ Block_driver driver { env, heap, announce_dispatcher, request_handler };
+
+ void announce()
{
- Env &env;
- Allocator &alloc;
- Signal_context_capability sigh;
- Usb::Block_driver driver;
+ env.parent().announce(env.ep().manage(*this));
+ }
- Factory(Env &env, Allocator &alloc,
- Signal_context_capability sigh)
- : env(env), alloc(alloc), sigh(sigh),
- driver(env, alloc, sigh) { }
+ /**
+ * There can only be one request in flight for this driver, so keep it simple
+ */
+ void handle_requests()
+ {
+ if (!block_session.constructed()) return;
- Block::Driver *create() override { return &driver; }
+ /* ack and release possibly pending packet */
+ block_session->try_acknowledge([&] (Block_session_component::Ack &ack) {
+ driver.with_completed([&] (Block::Request &request) {
+ ack.submit(request);
+ });
+ });
- void destroy(Block::Driver *) override { }
+ block_session->with_requests([&] (Request request) {
- private:
+ /* only read/write for now */
+ if (Operation::has_payload(request.operation.type) == false) {
+ request.success = true;
+ return Response::REJECTED;
+ }
- /*
- * Noncopyable
- */
- Factory(Factory const &);
- Factory &operator = (Factory const &);
- };
+ Response response = Response::RETRY;
- Factory factory { env, heap, announce_dispatcher };
- Block::Root root { env.ep(), heap, env.rm(), factory, true };
+ block_session->with_payload([&] (Request_stream::Payload const &payload) {
+ response = driver.submit(request, payload);
+ });
+ return response;
+ });
+
+ block_session->wakeup_client_if_needed();
+ }
+
+
+ /*****************************
+ ** Block session interface **
+ *****************************/
+
+ Genode::Session_capability session(Root::Session_args const &args,
+ Affinity const &) override
+ {
+ log("new block session: ", args.string());
+
+ if (block_session.constructed()) {
+ error("device is already in use");
+ throw Service_denied();
+ }
+
+ size_t const ds_size =
+ Arg_string::find_arg(args.string(), "tx_buf_size").ulong_value(0);
+
+ Ram_quota const ram_quota = ram_quota_from_args(args.string());
+
+ if (ds_size >= ram_quota.value) {
+ warning("communication buffer size exceeds session quota");
+ throw Insufficient_ram_quota();
+ }
+
+ block_ds.construct(env.ram(), env.rm(), ds_size);
+ block_session.construct(env, block_ds->cap(), request_handler,
+ driver.info());
+
+ return block_session->cap();
+ }
+
+ void upgrade(Genode::Session_capability, Root::Upgrade_args const&) override { }
+
+ void close(Genode::Session_capability cap) override
+ {
+ if (!block_session.constructed() || !(block_session->cap() == cap))
+ return;
+
+ block_session.destruct();
+ block_ds.destruct();
+ }
Main(Env &env) : env(env) { }
};