mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-12 21:53:28 +00:00
usb_block: switch to Request_stream API
- remove old 'Driver' interface - implement Request stream front end - fix some namespacing issue #3661
This commit is contained in:
parent
a71ef16423
commit
6e54cad44c
@ -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 <base/allocator_avl.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/log.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/sleep.h>
|
||||
#include <block/component.h>
|
||||
#include <block/driver.h>
|
||||
#include <block_session/connection.h>
|
||||
#include <block/request_stream.h>
|
||||
#include <os/reporter.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <usb/usb.h>
|
||||
@ -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<Block_request> 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 <unsigned SIZE>
|
||||
static Genode::String<SIZE> get_label(Xml_node node)
|
||||
{
|
||||
static Genode::String<256> usb_label;
|
||||
Genode::String<SIZE> 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<Main> &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<Inquiry_response::Vid>(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<Main> &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<char*>(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 <typename FUNC>
|
||||
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::Session>,
|
||||
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> tx_cap() override { return Request_stream::tx_cap(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct Usb::Main : Rpc_object<Typed_root<Block::Session>>
|
||||
{
|
||||
Env &env;
|
||||
Heap heap { env.ram(), env.rm() };
|
||||
|
||||
void announce()
|
||||
{
|
||||
env.parent().announce(env.ep().manage(root));
|
||||
}
|
||||
Constructible<Attached_ram_dataspace> block_ds { };
|
||||
Constructible<Block_session_component> block_session { };
|
||||
|
||||
Signal_handler<Main> announce_dispatcher {
|
||||
env.ep(), *this, &Usb::Main::announce };
|
||||
|
||||
struct Factory : Block::Driver_factory
|
||||
Io_signal_handler<Main> 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) { }
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user