Remove Block::Driver interface

This commit removes the aged Block::Driver interface that has already
been superseded by the Block::Request_stream API for some time now.
It is solely used by components whose existence is made redundant,
like 'lx_block', or old drivers, like the SD-Card driver for PL180
and old i.MX53/6 systems or the RPi 1, that have not seen active use
other than in the nightly CI runs.

Fixes #5522.
This commit is contained in:
Josef Söntgen 2025-04-10 15:45:26 +02:00 committed by Norman Feske
parent 5fdfc4fcea
commit 76ad585e56
15 changed files with 0 additions and 2236 deletions

View File

@ -1,336 +0,0 @@
/*
* \brief Block-session component
* \author Christian Helmuth
* \author Stefan Kalkowski
* \date 2011-05-20
*/
/*
* Copyright (C) 2011-2017 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.
*/
#ifndef _INCLUDE__BLOCK__COMPONENT_H_
#define _INCLUDE__BLOCK__COMPONENT_H_
#include <base/log.h>
#include <base/component.h>
#include <base/allocator_avl.h>
#include <base/heap.h>
#include <root/component.h>
#include <block/driver.h>
namespace Block {
using namespace Genode;
class Session_component_base;
class Session_component;
class Root;
};
/**
* We have a hen and egg situation that makes this base class necessary.
* The Block::Session_rpc_object construction depends on a dataspace for
* the packet stream. The dataspace on the other hand is constructed by
* the driver, which is created on demand when creating a session.
* When creating the driver, and dataspace outside the Session_component
* constructor within _create_session of the root component, we would have
* to destroy the driver and dataspace within the destructor body of
* Session_component, which will lead to problems, because the packet stream
* destructors will be called after the shared memory already vanished.
*/
class Block::Session_component_base
{
protected:
Driver_factory &_driver_factory;
Driver &_driver;
Driver::Dma_buffer _rq;
Session_component_base(Driver_factory &factory, size_t tx_buf_size)
:
_driver_factory(factory),
_driver(*factory.create()),
_rq(_driver.alloc_dma_buffer(tx_buf_size, UNCACHED))
{ }
~Session_component_base()
{
_driver.free_dma_buffer(_rq.ds);
_driver_factory.destroy(&_driver);
}
};
class Block::Session_component : public Block::Session_component_base,
public Block::Driver_session
{
private:
addr_t _rq_phys;
Signal_handler<Session_component> _sink_ack;
Signal_handler<Session_component> _sink_submit;
bool _req_queue_full = false;
bool _ack_queue_full = false;
Packet_descriptor _p_to_handle { };
unsigned _p_in_fly;
Info const _info { _driver.info() };
bool const _writeable;
/**
* Acknowledge a packet already handled
*/
inline void _ack_packet(Packet_descriptor &packet)
{
if (!tx_sink()->ready_to_ack())
error("not ready to ack!");
tx_sink()->acknowledge_packet(packet);
_p_in_fly--;
}
/**
* Range check packet request
*/
inline bool _range_check(Packet_descriptor &p) {
return p.block_number() + p.block_count() - 1
< _info.block_count; }
/**
* Handle a single request
*/
void _handle_packet(Packet_descriptor packet)
{
_p_to_handle = packet;
_p_to_handle.succeeded(false);
/* ignore invalid packets */
bool const valid = _range_check(_p_to_handle)
&& tx_sink()->packet_valid(packet)
&& aligned(packet.offset(), (unsigned)_info.align_log2);
if (!valid) {
_ack_packet(_p_to_handle);
return;
}
try {
switch (_p_to_handle.operation()) {
case Block::Packet_descriptor::READ:
if (_driver.dma_enabled())
_driver.read_dma(packet.block_number(),
packet.block_count(),
_rq_phys + packet.offset(),
_p_to_handle);
else
_driver.read(packet.block_number(),
packet.block_count(),
tx_sink()->packet_content(packet),
_p_to_handle);
break;
case Block::Packet_descriptor::WRITE:
if (!_writeable) {
_ack_packet(_p_to_handle);
break;
}
if (_driver.dma_enabled())
_driver.write_dma(packet.block_number(),
packet.block_count(),
_rq_phys + packet.offset(),
_p_to_handle);
else
_driver.write(packet.block_number(),
packet.block_count(),
tx_sink()->packet_content(packet),
_p_to_handle);
break;
case Block::Packet_descriptor::SYNC:
/* perform (blocking) sync */
_driver.sync();
_p_to_handle.succeeded(true);
_ack_packet(_p_to_handle);
_p_to_handle = Packet_descriptor();
break;
case Block::Packet_descriptor::TRIM:
/* trim is a nop */
_p_to_handle.succeeded(true);
_ack_packet(_p_to_handle);
_p_to_handle = Packet_descriptor();
break;
default:
throw Driver::Io_error();
}
} catch (Driver::Request_congestion) {
_req_queue_full = true;
} catch (Driver::Io_error) {
_ack_packet(_p_to_handle);
} catch (Genode::Packet_descriptor::Invalid_packet) {
_p_to_handle = Packet_descriptor();
Genode::error("dropping invalid Block packet");
}
}
/**
* Called whenever a signal from the packet-stream interface triggered
*/
void _signal()
{
/*
* as long as more packets are available, and we're able to ack
* them, and the driver's request queue isn't full,
* direct the packet request to the driver backend
*/
for (_ack_queue_full = (_p_in_fly >= tx_sink()->ack_slots_free());
!_req_queue_full && !_ack_queue_full
&& tx_sink()->packet_avail();
_ack_queue_full = (++_p_in_fly >= tx_sink()->ack_slots_free()))
_handle_packet(tx_sink()->get_packet());
}
public:
/**
* Constructor
*
* \param driver_factory factory to create and destroy driver objects
* \param ep entrypoint handling this session component
* \param buf_size size of packet-stream payload buffer
*/
Session_component(Driver_factory &driver_factory,
Genode::Entrypoint &ep,
Genode::Env::Local_rm &rm,
size_t buf_size,
bool writeable)
: Session_component_base(driver_factory, buf_size),
Driver_session(rm, _rq.ds, ep.rpc_ep()),
_rq_phys(_rq.dma_addr),
_sink_ack(ep, *this, &Session_component::_signal),
_sink_submit(ep, *this, &Session_component::_signal),
_req_queue_full(false),
_p_in_fly(0),
_writeable(writeable && _info.writeable)
{
_tx.sigh_ready_to_ack(_sink_ack);
_tx.sigh_packet_avail(_sink_submit);
_driver.session(this);
}
~Session_component() { _driver.session(nullptr); }
/**
* Acknowledges a packet processed by the driver to the client
*
* \param packet the packet to acknowledge
* \param success indicated whether the processing was successful
*
* \throw Ack_congestion
*/
void ack_packet(Packet_descriptor &packet, bool success) override
{
packet.succeeded(success);
_ack_packet(packet);
if (!_req_queue_full && !_ack_queue_full)
return;
/*
* when the driver's request queue was full,
* handle last unprocessed packet taken out of submit queue
*/
if (_req_queue_full) {
_req_queue_full = false;
_handle_packet(_p_to_handle);
}
/* resume packet processing */
_signal();
}
/*******************************
** Block session interface **
*******************************/
Info info() const override { return _driver.info(); }
};
/**
* Root component, handling new session requests
*/
class Block::Root : public Genode::Root_component<Block::Session_component,
Single_client>
{
private:
Driver_factory &_driver_factory;
Genode::Entrypoint &_ep;
Genode::Env::Local_rm &_rm;
bool const _writeable;
protected:
/**
* Always returns the singleton block-session component
*/
Session_component *_create_session(const char *args) override
{
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
/*
* Check if donated ram quota suffices for both
* communication buffers. Also check both sizes separately
* to handle a possible overflow of the sum of both sizes.
*/
if (tx_buf_size > ram_quota) {
error("insufficient 'ram_quota', got ", ram_quota, ", need ",
tx_buf_size);
throw Insufficient_ram_quota();
}
bool writeable = _writeable
? Arg_string::find_arg(args, "writeable").bool_value(true)
: false;
return new (md_alloc()) Session_component(_driver_factory,
_ep, _rm, tx_buf_size,
writeable);
}
public:
/**
* Constructor
*
* \param ep entrypoint handling this root component
* \param md_alloc allocator to allocate session components
* \param rm region map
* \param driver_factory factory to create and destroy driver backend
*/
Root(Genode::Entrypoint &ep,
Allocator &md_alloc,
Genode::Env::Local_rm &rm,
Driver_factory &driver_factory,
bool writeable)
:
Root_component(ep, md_alloc),
_driver_factory(driver_factory), _ep(ep), _rm(rm), _writeable(writeable)
{ }
};
#endif /* _INCLUDE__BLOCK__COMPONENT_H_ */

View File

@ -1,261 +0,0 @@
/*
* \brief Block-driver interface
* \author Christian Helmuth
* \author Sebastian Sumpf
* \author Stefan kalkowski
* \date 2011-05-23
*/
/*
* Copyright (C) 2011-2017 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.
*/
#ifndef _INCLUDE__BLOCK__DRIVER_H_
#define _INCLUDE__BLOCK__DRIVER_H_
#include <base/exception.h>
#include <base/stdint.h>
#include <base/signal.h>
#include <base/ram_allocator.h>
#include <block_session/rpc_object.h>
namespace Block {
class Driver_session_base;
class Driver_session;
class Driver;
struct Driver_factory;
};
struct Block::Driver_session_base : Genode::Interface
{
/**
* Acknowledges a packet processed by the driver to the client
*
* \param packet the packet to acknowledge
* \param success indicated whether the processing was successful
*
* \throw Ack_congestion
*/
virtual void ack_packet(Packet_descriptor &packet,
bool success) = 0;
};
class Block::Driver_session : public Driver_session_base,
public Block::Session_rpc_object
{
public:
/**
* Constructor
*
* \param rm region map of local address space, used to attach
* the packet-stream buffer to the local address space
* \param tx_ds dataspace used as communication buffer
* for the tx packet stream
* \param ep entry point used for packet-stream channel
*/
Driver_session(Genode::Env::Local_rm &rm,
Genode::Dataspace_capability tx_ds,
Genode::Rpc_entrypoint &ep)
: Session_rpc_object(rm, tx_ds, ep) { }
};
/**
* Interface to be implemented by the device-specific driver code
*/
class Block::Driver : Genode::Interface
{
private:
/*
* Noncopyable
*/
Driver(Driver const &);
Driver &operator = (Driver const &);
Genode::Ram_allocator &_ram;
Driver_session_base *_session = nullptr;
public:
/**
* Exceptions
*/
class Io_error : public ::Genode::Exception { };
class Request_congestion : public ::Genode::Exception { };
/**
* Constructor
*/
Driver(Genode::Ram_allocator &ram) : _ram(ram) { }
/**
* Destructor
*/
virtual ~Driver() { }
/**
* Request block-device information
*/
virtual Session::Info info() const = 0;
/**
* Read from medium
*
* \param block_number number of first block to read
* \param block_count number of blocks to read
* \param buffer output buffer for read request
* \param packet packet descriptor from the client
*
* \throw Request_congestion
*
* Note: should be overridden by DMA non-capable devices
*/
virtual void read(sector_t /* block_number */,
Genode::size_t /* block_count */,
char * /* buffer */,
Packet_descriptor & /* packet */) {
throw Io_error(); }
/**
* Write to medium
*
* \param block_number number of first block to write
* \param block_count number of blocks to write
* \param buffer buffer for write request
* \param packet packet descriptor from the client
*
* \throw Request_congestion
*
* Note: should be overridden by DMA non-capable, non-ROM devices
*/
virtual void write(sector_t /* block_number */,
Genode::size_t /* block_count */,
const char * /* buffer */,
Packet_descriptor & /* packet */) {
throw Io_error(); }
/**
* Read from medium using DMA
*
* \param block_number number of first block to read
* \param block_count number of blocks to read
* \param phys phyiscal address of read buffer
* \param packet packet descriptor from the client
*
* \throw Request_congestion
*
* Note: should be overridden by DMA capable devices
*/
virtual void read_dma(sector_t /* block_number */,
Genode::size_t /* block_count */,
Genode::addr_t /* phys */,
Packet_descriptor & /* packet */) {
throw Io_error(); }
/**
* Write to medium using DMA
*
* \param block_number number of first block to write
* \param block_count number of blocks to write
* \param phys physical address of write buffer
* \param packet packet descriptor from the client
*
* \throw Request_congestion
*
* Note: should be overridden by DMA capable, non-ROM devices
*/
virtual void write_dma(sector_t /* block_number */,
Genode::size_t /* block_count */,
Genode::addr_t /* phys */,
Packet_descriptor & /* packet */) {
throw Io_error(); }
/**
* Check if DMA is enabled for driver
*
* \return true if DMA is enabled, false otherwise
*
* Note: has to be overriden by DMA-capable devices
*/
virtual bool dma_enabled() { return false; }
struct Dma_buffer
{
Genode::Ram_dataspace_capability ds;
Genode::addr_t dma_addr;
};
/**
* Allocate buffer which is suitable for DMA.
*
* Note: has to be overriden by DMA-capable devices
*/
virtual Dma_buffer alloc_dma_buffer(Genode::size_t, Genode::Cache) = 0;
/**
* Free buffer which is suitable for DMA.
*
* Note: has to be overriden by DMA-capable devices
*/
virtual void free_dma_buffer(Genode::Ram_dataspace_capability c) {
return _ram.free(c); }
/**
* Synchronize with device.
*
* Note: should be overriden by (e.g. intermediate) components,
* which cache data
*/
virtual void sync() {}
/**
* Informs the driver that the client session was closed
*
* Note: drivers with state (e.g. asynchronously working)
* should override this method, and reset their internal state
*/
virtual void session_invalidated() { }
/**
* Set single session component of the driver
*
* Session might get used to acknowledge requests.
*/
void session(Driver_session_base *session) {
if (!(_session = session)) session_invalidated(); }
/**
* Acknowledge a packet after processing finished to the client
*
* \param p packet to acknowledge
*/
void ack_packet(Packet_descriptor &p, bool success = true) {
if (_session) _session->ack_packet(p, success); }
};
/**
* Interface for constructing the driver object
*/
struct Block::Driver_factory : Genode::Interface
{
/**
* Construct new driver
*/
virtual Driver *create() = 0;
/**
* Destroy driver
*/
virtual void destroy(Driver *driver) = 0;
};
#endif /* _BLOCK__DRIVER_H_ */

View File

@ -1,76 +0,0 @@
assert {[have_board pbxa9]}
create_boot_directory
import_from_depot [depot_user]/src/[base_src] \
[depot_user]/src/init \
[depot_user]/src/platform \
[depot_user]/raw/pbxa9-devices
build { driver/sd_card app/block_tester }
install_config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default caps="100" ram="1M"/>
<start name="platform" managing_system="yes">
<provides><service name="Platform"/></provides>
<config>
<policy label="pl180_sd_card -> ">
<device name="mmc0"/>
</policy>
</config>
<route>
<any-service> <parent/> </any-service>
</route>
</start>
<start name="timer">
<provides><service name="Timer"/></provides>
<route> <any-service> <parent/> </any-service> </route>
</start>
<start name="pl180_sd_card">
<provides><service name="Block"/></provides>
<route>
<service name="ROM"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="RM"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<any-service> <any-child/> </any-service>
</route>
</start>
<start name="block_tester" ram="64M">
<config verbose="no" report="no" log="yes" stop_on_error="no">
<tests>
<sequential length="16M" size="1M" io_buffer="8M" batch="4"/>
<sequential start="4000" write="yes" length="16M" size="1M" io_buffer="8M" batch="4"/>
</tests>
</config>
<route> <any-service> <any-child/> <parent/> </any-service> </route>
</start>
</config> }
build_boot_image [build_artifacts]
set disk_image "bin/sd_card.img"
if { [file exists $disk_image] == 0 } then {
# create empty block device file
catch { exec dd if=/dev/zero of=$disk_image bs=1M count=512 }
}
append qemu_args "-drive file=$disk_image,format=raw,if=sd,cache=writeback -nographic "
run_genode_until {.*child "block_tester" exited with exit value 0.*\n} 30
exec rm $disk_image
# vi: set ft=tcl :

View File

@ -1,103 +0,0 @@
#
# Check for platform support
#
if {[have_cmd_switch --autopilot]} {
assert {![have_board virt_qemu_riscv] && ![have_board rpi]} \
"Autopilot mode is not supported on this platform."
}
proc buffer_size_kib {} {
if {[have_board pbxa9]} { return [expr 12 * 1024] }
if {[have_board imx6q_sabrelite]} { return [expr 1024] }
if {[have_board imx53_qsb]} { return [expr 1024] }
if {[have_board imx53_qsb_tz]} { return [expr 1024] }
if {[have_board rpi]} { return [expr 4 * 1024] }
assert {false} "Run script is not supported on this platform."
}
proc sd_card {} {
if {[have_board pbxa9]} { return pl180_sd_card }
if {[have_board imx6q_sabrelite]} { return imx6_sd_card }
if {[have_board imx53_qsb]} { return imx53_sd_card }
if {[have_board imx53_qsb_tz]} { return imx53_sd_card }
if {[have_board rpi]} { return rpi_sd_card }
assert {false} "Run script is not supported on this platform."
}
create_boot_directory
import_from_depot [depot_user]/src/[base_src] \
[depot_user]/src/init \
[depot_user]/src/platform
install_config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default caps="100" ram="1M"/>
<start name="platform" managing_system="yes">
<provides><service name="Platform"/></provides>
<config>
<policy label="sd_card -> "> <device name="mmc0"/> </policy>
</config>
<route> <any-service> <parent/> </any-service> </route>
</start>
<start name="timer">
<provides><service name="Timer"/></provides>
<route> <any-service> <parent/> </any-service> </route>
</start>
<start name="sd_card" ram="16M">
<binary name="} [sd_card] {"/>
<provides><service name="Block"/></provides>
<config benchmark="yes" buffer_size_kib="} [buffer_size_kib] {"/>
<route>
<service name="ROM"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="RM"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<any-service> <any-child/> </any-service>
</route>
</start>
</config> }
file copy [select_from_repositories board/[board]/devices] [run_dir]/genode/devices
build { driver/sd_card }
build_boot_image [build_artifacts]
#
# Run and evaluate the test
#
# If we run on qemu, provide a virtual disk
#
set disk_image "bin/sd_card.img"
if {[have_include power_on/qemu]} {
if { [file exists $disk_image] == 0 } then {
catch { exec dd if=/dev/zero of=$disk_image bs=1M count=512 } }
append qemu_args "-drive file=$disk_image,format=raw,if=sd,cache=writeback "
append qemu_args "-nographic "
}
run_genode_until "--- SD card benchmark finished ---" 120
if {[have_include power_on/qemu]} {
if { [file exists $disk_image] != 0 } then {
exec rm -f $disk_image
}
}

View File

@ -1,65 +0,0 @@
/*
* \brief Advanced DMA 2
* \author Martin Stein
* \date 2015-02-05
*/
/*
* Copyright (C) 2015-2017 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.
*/
/* Genode includes */
#include <cpu/memory_barrier.h>
#include <dataspace/client.h>
/* local includes */
#include <adma2.h>
using namespace Adma2;
Table::Table(Platform::Connection &platform)
:
_ds(platform, _ds_size, UNCACHED),
_base_virt(_ds.local_addr<Desc::access_t>())
{ }
int Table::setup_request(size_t const size, addr_t const buffer_phys)
{
/* sanity check */
static size_t constexpr max_size = _max_desc * Desc::Length::max;
if (size > max_size) {
Genode::error("block request too large");
return -1;
}
/* install new descriptors till they cover all requested bytes */
addr_t consumed = 0;
for (int index = 0; consumed < size; index++) {
/* clamp current request to maximum request size */
size_t const remaining = size - consumed;
size_t const curr = min(Desc::Length::max, remaining);
/* assemble new descriptor */
Desc::access_t desc = 0;
Desc::Address::set(desc, buffer_phys + consumed);
Desc::Length::set(desc, curr);
Desc::Act1::set(desc, 0);
Desc::Act2::set(desc, 1);
Desc::Valid::set(desc, 1);
/* let last descriptor generate transfer-complete signal */
if (consumed + curr == size) { Desc::End::set(desc, 1); }
/* install and account descriptor */
_base_virt[index] = desc;
consumed += curr;
}
/* ensure that all descriptor writes were actually executed */
Genode::memory_barrier();
return 0;
}

View File

@ -1,97 +0,0 @@
/*
* \brief Advanced DMA 2
* \author Martin Stein
* \date 2015-02-05
*/
/*
* Copyright (C) 2015-2017 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.
*/
#ifndef _ADMA2_H_
#define _ADMA2_H_
/* Genode includes */
#include <util/register.h>
#include <platform_session/dma_buffer.h>
namespace Adma2
{
using namespace Genode;
class Desc;
class Table;
}
/**
* Descriptor layout
*/
struct Adma2::Desc : Register<64>
{
struct Valid : Bitfield<0, 1> { };
struct End : Bitfield<1, 1> { };
struct Int : Bitfield<2, 1> { };
struct Act1 : Bitfield<4, 1> { };
struct Act2 : Bitfield<5, 1> { };
struct Length : Bitfield<16, 16>
{
/*
* According to the 'SD Specifications, Part A2, SD Host
* Controller, Simplified Specification, Version 2.00, February 8,
* 2007, Table 1-10', a maximum length of 65536 bytes is achieved
* by value 0. However, if we do so, the completion host-signal
* times out now and then. Thus, we use the next lower possible
* value.
*/
static constexpr addr_t align_log2 = 2;
static constexpr size_t max = (1 << WIDTH) - (1 << align_log2);
};
struct Address : Bitfield<32, 32> { };
};
/**
* Descriptor table
*/
class Adma2::Table
{
private:
static size_t constexpr _max_desc = 1024;
static size_t constexpr _ds_size = _max_desc * sizeof(Desc::access_t);
Platform::Dma_buffer _ds;
Desc::access_t * const _base_virt;
/*
* Noncopyable
*/
Table(Table const &);
Table &operator = (Table const &);
public:
Table(Platform::Connection &platform);
/**
* Marshal descriptors according to block request
*
* \param size request size in bytes
* \param buffer_phys physical base of transfer buffer
*
* \retval 0 success
* \retval -1 error
*/
int setup_request(size_t const size, addr_t const buffer_phys);
/*
* Accessors
*/
addr_t base_dma() const { return _ds.dma_addr(); }
};
#endif /* _ADMA2_H_ */

View File

@ -1,166 +0,0 @@
/*
* \brief SD-card benchmark
* \author Norman Feske
* \author Martin Stein
* \date 2012-07-19
*/
/*
* Copyright (C) 2012-2017 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.
*/
/* Genode includes */
#include <timer_session/connection.h>
#include <base/attached_ram_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <platform_session/dma_buffer.h>
/* local includes */
#include <driver.h>
using namespace Genode;
struct Benchmark
{
using Packet_descriptor = Block::Packet_descriptor;
struct Block_operation_failed : Exception { };
enum Operation { READ, WRITE };
struct Driver_session : Block::Driver_session_base
{
Signal_transmitter sig;
unsigned long nr_of_acks { 0 };
Driver_session(Signal_context_capability sig) : sig(sig) { }
void ack_packet(Packet_descriptor &, bool success) override
{
if (!success) {
throw Block_operation_failed(); }
nr_of_acks++;
sig.submit();
}
};
Env &env;
Platform::Connection platform { env };
Attached_rom_dataspace config { env, "config" };
Packet_descriptor pkt { };
uint64_t time_before_ms { };
Timer::Connection timer { env };
Operation operation { READ };
Signal_handler<Benchmark> ack_handler { env.ep(), *this, &Benchmark::update_state };
Driver_session drv_session { ack_handler };
Sd_card::Driver drv { env, platform };
size_t const buf_size_kib { config.xml().attribute_value("buffer_size_kib",
(size_t)0) };
size_t const buf_size { buf_size_kib * 1024 };
Platform::Dma_buffer buf { platform, buf_size, UNCACHED };
char *buf_virt { buf.local_addr<char>() };
size_t buf_off_done { 0 };
size_t buf_off_pend { 0 };
unsigned req_size_id { 0 };
size_t req_sizes[9] { 512, 1024, 1024 * 2, 1024 * 4,
1024 * 8, 1024 * 16, 1024 * 32,
1024 * 64, 1024 * 128 };
size_t req_size() const { return req_sizes[req_size_id]; }
void update_state()
{
/* raise done counter and check if the buffer is full */
buf_off_done += drv_session.nr_of_acks * req_size();
drv_session.nr_of_acks = 0;
if (buf_off_done == buf_size) {
/* print stats for the current request size */
uint64_t const time_after_ms = timer.elapsed_ms();
uint64_t const duration_ms = time_after_ms - time_before_ms;
size_t const kib_per_sec = (size_t)((1000 * buf_size_kib)
/ duration_ms);
log(" duration: ", duration_ms, " ms");
log(" amount: ", buf_size_kib, " KiB");
log(" throughput: ", kib_per_sec, " KiB/sec");
/* go to next request size */
buf_off_pend = 0;
buf_off_done = 0;
req_size_id++;
/* check if we have done all request sizes for an operation */
if (req_size_id == sizeof(req_sizes)/sizeof(req_sizes[0])) {
/* go to next operation or end the test */
log("");
req_size_id = 0;
switch (operation) {
case READ:
operation = WRITE;
log("-- writing to SD card --");
break;
case WRITE:
log("--- SD card benchmark finished ---");
return;
}
}
log(" request size ", req_size(), " bytes");
time_before_ms = timer.elapsed_ms();
}
/* issue as many requests for the current request size as possible */
try {
size_t const block_size = drv.info().block_size;
size_t const cnt = req_size() / block_size;
for (; buf_off_pend < buf_size; buf_off_pend += req_size()) {
/* calculate block offset */
addr_t const nr = buf_off_pend / block_size;
if (drv.dma_enabled()) {
/* request with DMA */
addr_t const phys = buf.dma_addr() + buf_off_pend;
switch (operation) {
case READ: drv.read_dma(nr, cnt, phys, pkt); break;
case WRITE: drv.write_dma(nr, cnt, phys, pkt); break; }
} else {
/* request without DMA */
char *const virt = buf_virt + buf_off_pend;
switch (operation) {
case READ: drv.read(nr, cnt, virt, pkt); break;
case WRITE: drv.write(nr, cnt, virt, pkt); break; }
}
}
} catch (Block::Driver::Request_congestion) { }
}
Benchmark(Env &env) : env(env)
{
log("");
log("--- SD card benchmark (", drv.dma_enabled() ? "with" : "no", " DMA) ---");
drv.session(&drv_session);
/* start issuing requests */
log("");
log("-- reading from SD card --");
log(" request size ", req_size(), " bytes");
time_before_ms = timer.elapsed_ms();
update_state();
}
private:
/*
* Noncopyable
*/
Benchmark(Benchmark const &);
Benchmark &operator = (Benchmark const &);
};

View File

@ -1,56 +0,0 @@
/*
* \brief Generic parts of an SD card block-driver
* \author Norman Feske
* \author Timo Wischer
* \author Martin Stein
* \date 2017-01-03
*/
/*
* Copyright (C) 2017 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.
*/
#ifndef _DRIVER_BASE_H_
#define _DRIVER_BASE_H_
/* Genode includes */
#include <block/component.h>
/* local includes */
#include <sd_card.h>
namespace Sd_card { class Driver_base; }
class Sd_card::Driver_base : public Block::Driver,
protected Host_controller
{
protected:
Genode::size_t _block_size() const { return 512; }
Block::sector_t _block_count() const {
return card_info().capacity_mb() * 1024 * 2; }
public:
Driver_base(Genode::Ram_allocator &ram)
: Block::Driver(ram) { }
/*******************
** Block::Driver **
*******************/
Block::Session::Info info() const override
{
return { .block_size = _block_size(),
.block_count = _block_count(),
.align_log2 = Genode::log2(_block_size()),
.writeable = true };
}
};
#endif /* _DRIVER_BASE_H_ */

View File

@ -1,71 +0,0 @@
/*
* \brief SD-card driver
* \author Martin Stein
* \author Sebastian Sumpf
* \date 2013-03-06
*/
/*
* Copyright (C) 2013-2017 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.
*/
/* Genode includes */
#include <base/component.h>
#include <base/log.h>
#include <base/heap.h>
#include <block/component.h>
#include <platform_session/connection.h>
/* local includes */
#include <benchmark.h>
#include <driver.h>
using namespace Genode;
struct Main
{
Env & env;
Heap heap { env.ram(), env.rm() };
Platform::Connection platform { env };
struct Factory : Block::Driver_factory
{
Env & env;
Heap & heap;
Platform::Connection & platform;
Factory(Env &env, Heap &heap, Platform::Connection &platform)
: env(env), heap(heap), platform(platform) { }
Block::Driver *create() override {
return new (&heap) Sd_card::Driver(env, platform); }
void destroy(Block::Driver *driver) override {
Genode::destroy(&heap, static_cast<Sd_card::Driver*>(driver)); }
} factory { env, heap, platform };
Block::Root root { env.ep(), heap, env.rm(), factory, true };
Main(Genode::Env &env) : env(env)
{
Genode::log("--- SD card driver ---");
env.parent().announce(env.ep().manage(root));
}
};
void Component::construct(Genode::Env &env)
{
bool benchmark = false;
try {
Attached_rom_dataspace config { env, "config" };
benchmark = config.xml().attribute_value("benchmark", false);
} catch(...) {}
if (benchmark) static Benchmark bench(env);
else static Main main(env);
}

View File

@ -1,240 +0,0 @@
/*
* \brief PL180-specific implementation of the Block::Driver interface
* \author Christian Helmuth
* \author Martin Stein
* \date 2011-05-19
*/
/*
* Copyright (C) 2011-2017 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.
*/
/* local includes */
#include <driver.h>
using namespace Genode;
using namespace Sd_card;
void Driver::_write_command(unsigned cmd_index, bool resp)
{
enum Bits {
CmdIndex = 0x3f,
Response = 1 << 6,
Enable = 1 << 10
};
cmd_index = (cmd_index & CmdIndex) | Enable;
cmd_index |= (resp ? Response : 0);
_write_reg(Command, cmd_index);
while (!(_read_reg(Status) & (CmdRespEnd | CmdSent))) ;
}
void Driver::_request(unsigned char cmd,
unsigned *out_resp)
{
_write_reg(Argument, 0);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_request(unsigned char cmd,
unsigned arg,
unsigned *out_resp)
{
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_read_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp)
{
/*
* FIXME on real hardware the blocksize must be written into
* DataCtrl:BlockSize.
*/
enum { CTRL_ENABLE = 0x01, CTRL_READ = 0x02 };
_write_reg(DataLength, length);
_write_reg(DataCtrl, CTRL_ENABLE | CTRL_READ);
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_write_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp)
{
/*
* FIXME on real hardware the blocksize must be written into
* DataCtrl:BlockSize.
*/
enum { CTRL_ENABLE = 0x01 };
_write_reg(DataLength, length);
_write_reg(DataCtrl, CTRL_ENABLE);
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_read_data(unsigned length,
char *out_buffer)
{
unsigned *buf = reinterpret_cast<unsigned *>(out_buffer);
for (unsigned count = 0; count < length / 4; ) {
/*
* The FIFO contains at least 'remainder - FifoCnt' words.
*/
int chunk = length / 4 - count - _read_reg(FifoCnt);
count += chunk;
for ( ; chunk > 0; --chunk)
buf[count - chunk] = _read_reg(FIFO);
}
_clear_status();
}
void Driver::_write_data(unsigned length,
char const *buffer)
{
enum { FIFO_SIZE = 16 };
unsigned const *buf = reinterpret_cast<unsigned const *>(buffer);
for (unsigned count = 0; count < length / 4; ) {
uint32_t status;
while (!((status = _read_reg(Status)) & TxFifoHalfEmpty)) ;
int chunk = (status & TxFifoEmpty) ? FIFO_SIZE : FIFO_SIZE / 2;
count += chunk;
for ( ; chunk > 0; --chunk)
_write_reg(FIFO, buf[count - chunk]);
}
_clear_status();
}
Driver::Driver(Env &env, Platform::Connection & platform)
:
Block::Driver(env.ram()),
Platform::Device(platform),
Platform::Device::Mmio<SIZE>(*this, { 0 }),
_platform(platform),
_timer(env)
{
enum { POWER_UP = 2, POWER_ON = 3 };
_write_reg(Power, POWER_UP);
_timer.msleep(10);
_write_reg(Power, POWER_ON);
_timer.msleep(10);
_clear_status();
/* CMD0: go idle state */
_request(0, 0);
/*
* CMD8: send interface condition
*
* XXX only one hard-coded value currently.
*/
unsigned resp;
_request(8, 0x1aa, &resp);
/*
* ACMD41: card send operating condition
*
* This is an application-specific command and, therefore, consists
* of prefix command CMD55 + CMD41.
*/
_request(55, 0, &resp);
_request(41, 0x4000, &resp);
/* CMD2: all send card identification (CID) */
_request(2, &resp);
/* CMD3: send relative card address (RCA) */
_request(3, &resp);
unsigned short rca = (short)(resp >> 16);
/*
* Now, the card is in transfer mode...
*/
/* CMD7: select card */
_request(7, rca << 16, &resp);
}
Driver::~Driver() { }
void Driver::read(Block::sector_t block_number,
size_t block_count,
char *buffer,
Block::Packet_descriptor &packet)
{
unsigned resp;
unsigned length = _block_size;
for (size_t i = 0; i < block_count; ++i) {
/*
* CMD17: read single block
*
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
* block address here.
*/
_read_request(17, (uint32_t)((block_number + i) * _block_size),
length, &resp);
_read_data(length, buffer + (i * _block_size));
}
ack_packet(packet);
}
void Driver::write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet)
{
unsigned resp;
unsigned length = _block_size;
for (size_t i = 0; i < block_count; ++i) {
/*
* CMD24: write single block
*
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
* block address here.
*/
_write_request(24, (uint32_t)((block_number + i) * _block_size),
length, &resp);
_write_data(length, buffer + (i * _block_size));
}
ack_packet(packet);
}

View File

@ -1,161 +0,0 @@
/*
* \brief PL180-specific implementation of the Block::Driver interface
* \author Christian Helmuth
* \author Martin Stein
* \date 2011-05-19
*/
/*
* Copyright (C) 2011-2017 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.
*/
#ifndef _DRIVER_H_
#define _DRIVER_H_
/* local includes */
#include <block/driver.h>
#include <platform_session/device.h>
#include <timer_session/connection.h>
namespace Sd_card {
using namespace Genode;
class Driver;
}
class Sd_card::Driver : public Block::Driver,
private Platform::Device,
private Platform::Device::Mmio<0>
{
private:
/*
* Noncopyable
*/
Driver(Driver const &);
Driver &operator = (Driver const &);
enum Register {
Power = 0x000, /* power control */
Argument = 0x008, /* argument for command */
Command = 0x00c, /* command index and type */
Response0 = 0x014, /* command response (card status, read only) */
DataLength = 0x028, /* number of bytes in data transfer (block size) */
DataCtrl = 0x02c, /* data transfer control */
Status = 0x034, /* controller status flags (read only) */
Clear = 0x038, /* status clear (write only) */
Mask0 = 0x03c, /* interrupt 0 mask */
Mask1 = 0x040, /* interrupt 1 mask */
FifoCnt = 0x048, /* data FIFO counter (in words, read only) */
FIFO = 0x080, /* data FIFO */
};
enum Flag {
CmdCrcFail = 0x000001, /* command response received (CRC failed) */
DataCrcFail = 0x000002, /* data block sent/received (CRC failed) */
CmdTimeOut = 0x000004, /* command response timeout */
DataTimeOut = 0x000008, /* data timeout */
TxUnderrun = 0x000010, /* tx fifo underrun */
RxUnderrun = 0x000020, /* rx fifo underrun */
CmdRespEnd = 0x000040, /* command response received (CRC ok) */
CmdSent = 0x000080, /* command sent (no response required) */
DataEnd = 0x000100, /* data counter zero */
StartBitErr = 0x000200, /* start bit not detected */
DataBlockEnd = 0x000400, /* data block sent/received (CRC ok) */
CmdActive = 0x000800, /* command transfer in progress */
TxActive = 0x001000, /* data tx in progress */
RxActive = 0x002000, /* data rx in progress */
TxFifoHalfEmpty = 0x004000,
RxFifoHalfFull = 0x008000,
TxFifoFull = 0x010000,
RxFifoFull = 0x020000,
TxFifoEmpty = 0x040000,
RxFifoEmpty = 0x080000,
TxDataAvlbl = 0x100000,
RxDataAvlbl = 0x200000,
};
Platform::Connection & _platform;
Timer::Connection _timer;
uint32_t volatile *_base { local_addr<unsigned volatile>() };
uint32_t _read_reg(Register reg) const { return _base[reg >> 2]; }
void _write_reg(Register reg, uint32_t v) { _base[reg >> 2] = v; }
void _clear_status() { _write_reg(Clear, ~0); }
void _request(unsigned char cmd,
unsigned *out_resp);
void _request(unsigned char cmd,
unsigned arg,
unsigned *out_resp);
void _read_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp);
void _write_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp);
void _read_data(unsigned length, char *out_buffer);
void _write_data(unsigned length, char const *buffer);
void _write_command(unsigned cmd_index, bool resp);
/*
* TODO report (and support) real capacity not just 512M
*/
size_t const _block_size = 512;
Block::sector_t const _block_count = 0x20000000 / _block_size;
public:
Driver(Env &env, Platform::Connection & platform);
~Driver();
/******************
** Block-driver **
******************/
Dma_buffer alloc_dma_buffer(size_t size, Cache cache) override
{
Ram_dataspace_capability ds =
_platform.retry_with_upgrade(Ram_quota{4096}, Cap_quota{2},
[&] () { return _platform.alloc_dma_buffer(size, cache); });
return { .ds = ds,
.dma_addr = _platform.dma_addr(ds) };
}
Block::Session::Info info() const override
{
return { .block_size = _block_size,
.block_count = _block_count,
.align_log2 = log2(_block_size),
.writeable = true };
}
void read(Block::sector_t block_number,
size_t block_count,
char *buffer,
Block::Packet_descriptor &packet) override;
void write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet) override;
};
#endif /* _DRIVER_H_ */

View File

@ -1,4 +0,0 @@
TARGET = pl180_sd_card
REQUIRES = arm_v7
include $(REP_DIR)/src/driver/sd_card/target.inc

View File

@ -1,593 +0,0 @@
/*
* \brief SD card protocol definitions
* \author Norman Feske
* \author Sebastian Sumpf
* \date 2012-07-06
*/
/*
* Copyright (C) 2012-2017 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.
*/
#ifndef _SD_CARD_H_
#define _SD_CARD_H_
/* Genode includes */
#include <util/mmio.h>
namespace Sd_card {
using namespace Genode;
/**
* Structure of the first word of a native mode R1 response
*/
struct R1_response_0 : Register<32>
{
struct Ready_for_data : Bitfield<8, 1> { };
struct State : Bitfield<9, 4>
{
enum { PROGRAM = 7 };
};
struct Error : Bitfield<19, 1> { };
/**
* Return wether the card is ready for data
*
* \param resp R1 response, word 0
*/
static bool card_ready(access_t const resp)
{
/*
* Check both ready bit and state because not all cards handle
* the status bits correctly.
*/
return Ready_for_data::get(resp) &&
State::get(resp) != State::PROGRAM;
}
};
/**
* Returned by 'Sd_send_op_cond'
*/
struct Ocr : Register<32>
{
struct Busy : Bitfield<31, 1> { };
};
struct Cid
{
uint32_t raw_0, raw_1, raw_2, raw_3;
void print(Output &out) const
{
Genode::print(out, Hex(raw_3), " ", Hex(raw_2), " ",
Hex(raw_1), " ", Hex(raw_0));
}
};
struct Csd0 : Register<32>
{
};
struct Csd1 : Register<32>
{
enum { BIT_BASE = 1*sizeof(access_t)*8 };
struct V2_device_size_lo : Bitfield<48 - BIT_BASE, 16> { };
struct V1_c_size_lo : Bitfield<62 - BIT_BASE, 2> { };
struct V1_c_size_mult : Bitfield<47 - BIT_BASE, 3> { };
};
struct Csd2 : Register<32>
{
enum { BIT_BASE = 2*sizeof(access_t)*8 };
struct V2_device_size_hi : Bitfield<64 - BIT_BASE, 6> { };
struct V1_read_bl_len : Bitfield<80 - BIT_BASE, 4> { };
struct V1_c_size_hi : Bitfield<64 - BIT_BASE, 10> { };
};
struct Csd3 : Register<32>
{
enum { BIT_BASE = 3*sizeof(access_t)*8 };
struct Version : Bitfield<126 - BIT_BASE, 2>
{
enum Type { STANDARD_CAPACITY = 0, HIGH_CAPACITY = 1, EXT_CSD = 3 };
};
struct Mmc_spec_vers : Bitfield<122 - BIT_BASE, 4> { };
};
struct Csd
{
Csd0::access_t csd0;
Csd1::access_t csd1;
Csd2::access_t csd2;
Csd3::access_t csd3;
};
struct Ext_csd : Mmio<0xd8>
{
using Mmio::Mmio;
struct Revision : Register<0xc0, 8> { };
struct Sector_count : Register<0xd4, 32> { };
};
struct Arg : Register<32> { };
enum Response { RESPONSE_NONE,
RESPONSE_136_BIT,
RESPONSE_48_BIT,
RESPONSE_48_BIT_WITH_BUSY };
enum Transfer { TRANSFER_NONE, TRANSFER_READ, TRANSFER_WRITE };
struct Command_base
{
unsigned index; /* command opcode */
Arg::access_t arg; /* argument */
Response rsp_type; /* response type */
Transfer transfer; /* data transfer type */
Command_base(unsigned op, Response rsp_type, Transfer transfer)
:
index(op), arg(0), rsp_type(rsp_type), transfer(transfer)
{ }
void print(Output &out) const
{
using Genode::print;
print(out, "index=", index, ", arg=", arg, ", rsp_type=");
switch (rsp_type) {
case RESPONSE_NONE: print(out, "NONE"); break;
case RESPONSE_136_BIT: print(out, "136_BIT"); break;
case RESPONSE_48_BIT: print(out, "48_BIT"); break;
case RESPONSE_48_BIT_WITH_BUSY: print(out, "48_BIT_WITH_BUSY"); break;
}
}
};
template <unsigned _INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
struct Command : Command_base
{
enum { INDEX = _INDEX };
Command() : Command_base(_INDEX, RSP_TYPE, TRANSFER) { }
};
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
struct Prefixed_command : private Command_base
{
Prefixed_command() : Command_base(INDEX, RSP_TYPE, TRANSFER) { }
using Command_base::arg;
/**
* Used by ACMD overload of 'issue_command()'
*/
Command_base const &base() const { return *this; }
};
struct Go_idle_state : Command<0, RESPONSE_NONE> { };
struct All_send_cid : Command<2, RESPONSE_136_BIT> { };
struct Send_relative_addr : Command<3, RESPONSE_48_BIT>
{
struct Response : Sd_card::Arg
{
struct Rca : Bitfield<16, 16> { };
};
Send_relative_addr(unsigned rca = 0)
{
Response::Rca::set(arg, rca);
}
};
struct Send_status : Command<13, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
struct Rca : Bitfield<16, 16> { };
};
};
struct Select_card : Command<7, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
struct Rca : Bitfield<16, 16> { };
};
Select_card(unsigned rca)
{
Arg::Rca::set(arg, rca);
}
};
struct Send_if_cond : Command<8, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
struct Check_pattern : Bitfield<0, 8> { };
struct Supply_voltage : Bitfield<8, 4> { };
};
Send_if_cond()
{
Arg::Check_pattern::set(arg, 0xaa);
Arg::Supply_voltage::set(arg, 1);
}
};
struct Send_csd : Command<9, RESPONSE_136_BIT>
{
struct Arg : Sd_card::Arg
{
struct Rca : Bitfield<16, 16> { };
};
Send_csd(unsigned rca)
{
Arg::Rca::set(arg, rca);
}
};
struct Mmc_send_ext_csd : Command<8, RESPONSE_48_BIT_WITH_BUSY, TRANSFER_READ>
{ };
struct Set_block_count : Command<23, RESPONSE_48_BIT>
{
Set_block_count(size_t count)
{
arg = count;
};
};
struct Set_blocklen : Command<16, RESPONSE_48_BIT>
{
Set_blocklen(size_t blocklen)
{
arg = blocklen;
};
};
struct Read_multiple_block : Command<18, RESPONSE_48_BIT, TRANSFER_READ>
{
Read_multiple_block(unsigned long addr)
{
arg = addr;
}
};
struct Write_multiple_block : Command<25, RESPONSE_48_BIT, TRANSFER_WRITE>
{
Write_multiple_block(unsigned long addr)
{
arg = addr;
}
};
struct Set_bus_width : Prefixed_command<6, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
struct Bus_width : Bitfield<0, 2>
{
enum Width { ONE_BIT = 0, FOUR_BITS = 2 };
};
};
Set_bus_width(Arg::Bus_width::Width width)
{
Arg::Bus_width::set(arg, width);
}
};
struct Mmc_switch : Command<6, RESPONSE_48_BIT>
{
enum { SWITCH_MODE_WRITE_BYTE = 0x3 };
struct Arg : Sd_card::Arg
{
struct Value : Bitfield<8, 8> { };
struct Index : Bitfield<16, 8> { };
struct Mode : Bitfield<24, 8> { };
};
Mmc_switch(unsigned index, unsigned val)
{
Arg::Mode::set(arg, SWITCH_MODE_WRITE_BYTE);
Arg::Index::set(arg, index);
Arg::Value::set(arg, val);
}
};
struct Sd_send_op_cond : Prefixed_command<41, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
/**
* Operating condition register
*/
struct Ocr : Bitfield<0, 24> { };
/**
* Host capacity support
*/
struct Hcs : Bitfield<30, 1> { };
};
Sd_send_op_cond(unsigned ocr, bool hcs)
{
Arg::Ocr::set(arg, ocr);
Arg::Hcs::set(arg, hcs);
}
};
struct Mmc_send_op_cond : Command<1, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
/**
* Operating condition register
*/
struct Ocr : Bitfield<0, 24> { };
/**
* Host capacity support
*/
struct Hcs : Bitfield<30, 1> { };
};
Mmc_send_op_cond(unsigned ocr, bool hcs)
{
Arg::Ocr::set(arg, ocr);
Arg::Hcs::set(arg, hcs);
}
};
struct Stop_transmission : Command<12, RESPONSE_48_BIT> { };
struct Acmd_prefix : Command<55, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
struct Rca : Bitfield<16, 16> { };
};
Acmd_prefix(unsigned rca)
{
Arg::Rca::set(arg, rca);
}
};
class Card_info
{
private:
unsigned _rca;
size_t _capacity_mb;
const Csd3::Version::Type _version;
public:
Card_info(unsigned rca, size_t capacity_mb, const Csd3::Version::Type version)
: _rca(rca), _capacity_mb(capacity_mb), _version(version)
{ }
/**
* Return capacity in megabytes
*/
size_t capacity_mb() const { return _capacity_mb; }
/**
* Return relative card address
*/
unsigned rca() const { return _rca; }
/**
* Returns the version of the card
*/
Csd3::Version::Type version() const { return _version; }
};
/**
* SD card host controller
*/
class Host_controller : Genode::Interface
{
public:
/**
* Exception type
*/
struct Detection_failed { };
protected:
virtual bool _issue_command(Command_base const &command) = 0;
virtual Cid _read_cid() = 0;
virtual Csd _read_csd() = 0;
virtual unsigned _read_rca() = 0;
virtual size_t _read_ext_csd() { return 0; }
public:
virtual Card_info card_info() const = 0;
bool issue_command(Command_base const &command)
{
return _issue_command(command);
}
/**
* Issue application-specific command
*
* This overload is selected if the supplied command type has
* 'Prefixed_command' as its base class. In this case, we need to
* issue a CMD55 as command prefix followed by the actual command.
*
* \param prefix_rca argument to CMD55 prefix command
*/
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER>
bool issue_command(Prefixed_command<INDEX, RSP_TYPE, TRANSFER> const &command,
unsigned prefix_rca = 0)
{
/* send CMD55 prefix */
if (!_issue_command(Acmd_prefix(prefix_rca))) {
error("prefix command timed out");
return false;
}
/* send actual command */
return _issue_command(command.base());
}
private:
/**
* Extract capacity information from CSD register
*
* \throw Detection_failed
* \return capacity in 512-kByte blocks
*/
size_t _sd_card_device_size(Csd const csd)
{
/*
* The capacity is reported via the CSD register. There are
* two versions of this register. Standard-capacity cards
* use version 1 whereas high-capacity cards use version 2.
*/
if (Csd3::Version::get(csd.csd3) == Csd3::Version::STANDARD_CAPACITY) {
/*
* Calculation of the capacity according to the
* "Physical Layer Simplified Specification Version 4.10",
* Section 5.3.2.
*/
size_t const read_bl_len = Csd2::V1_read_bl_len::get(csd.csd2);
size_t const c_size = (Csd2::V1_c_size_hi::get(csd.csd2) << 2)
| Csd1::V1_c_size_lo::get(csd.csd1);
size_t const c_size_mult = Csd1::V1_c_size_mult::get(csd.csd1);
size_t const mult = 1 << (c_size_mult + 2);
size_t const block_len = 1 << read_bl_len;
size_t const capacity = (c_size + 1)*mult*block_len;
size_t const capacity512KiB = capacity / (512 * 1024);
return capacity512KiB;
}
if (Csd3::Version::get(csd.csd3) == Csd3::Version::HIGH_CAPACITY)
return ((Csd2::V2_device_size_hi::get(csd.csd2) << 16)
| Csd1::V2_device_size_lo::get(csd.csd1)) + 1;
error("Could not detect SD-card capacity");
throw Detection_failed();
}
protected:
/**
* Perform SD card detection sequence
*
* \throw Detection_failed
*/
Card_info _detect()
{
if (!issue_command(All_send_cid())) {
warning("All_send_cid command failed");
throw Detection_failed();
}
Cid const cid = _read_cid();
log("CID: ", cid);
if (!issue_command(Send_relative_addr())) {
error("Send_relative_addr timed out");
throw Detection_failed();
}
unsigned const rca = _read_rca();
log("RCA: ", Hex(rca));
if (!issue_command(Send_csd(rca))) {
error("Send_csd failed");
throw Detection_failed();
}
Csd const csd = _read_csd();
if (!issue_command(Select_card(rca))) {
error("Select_card failed");
throw Detection_failed();
}
return Card_info(rca, _sd_card_device_size(csd) / 2,
static_cast<Csd3::Version::Type>(Csd3::Version::get(csd.csd3)));
}
Card_info _detect_mmc()
{
if (!issue_command(All_send_cid())) {
warning("All_send_cid command failed");
throw Detection_failed();
}
unsigned const rca = 1;
if (!issue_command(Send_relative_addr(rca))) {
error("Send_relative_addr timed out");
throw Detection_failed();
}
if (!issue_command(Send_csd(rca))) {
error("Send_csd failed");
throw Detection_failed();
}
Csd const csd = _read_csd();
if (Csd3::Version::get(csd.csd3) != Csd3::Version::EXT_CSD) {
error("Csd version is not extented CSD");
throw Detection_failed();
}
if (Csd3::Mmc_spec_vers::get(csd.csd3) < 4) {
error("Csd specific version is less than 4");
throw Detection_failed();
}
if (!issue_command(Select_card(rca))) {
error("Select_card failed");
throw Detection_failed();
}
size_t device_size;
if(!(device_size = _read_ext_csd())) {
error("Could not read extented CSD");
throw Detection_failed();
}
return Card_info(rca, device_size,
static_cast<Csd3::Version::Type>(Csd3::Version::get(csd.csd3)));
}
};
}
#endif /* _SD_CARD_H_ */

View File

@ -1,6 +0,0 @@
SRC_CC += main.cc driver.cc
LIBS += base
INC_DIR += $(PRG_DIR) $(REP_DIR)/src/driver/sd_card
vpath %.cc $(REP_DIR)/src/driver/sd_card
vpath %.cc $(PRG_DIR)

View File

@ -64,7 +64,6 @@ nova
platform
rtc
rump_ext2
sd_card_bench
sd_card_zynq
seoul-auto
smartcard