196 lines
4.7 KiB
C++
196 lines
4.7 KiB
C++
/*
|
|
* \brief ATAPI protocol driver
|
|
* \author Sebastian Sumpf
|
|
* \date 2015-04-29
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2015-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.
|
|
*/
|
|
|
|
#ifndef _AHCI__ATAPI_PROTOCOL_H_
|
|
#define _AHCI__ATAPI_PROTOCOL_H_
|
|
|
|
#include "ahci.h"
|
|
#include <util/endian.h>
|
|
|
|
namespace Atapi {
|
|
class Protocol;
|
|
using namespace Ahci;
|
|
using namespace Genode;
|
|
}
|
|
|
|
class Atapi::Protocol : public Ahci::Protocol, Noncopyable
|
|
{
|
|
private:
|
|
|
|
Block::Request _pending { };
|
|
block_number_t _block_count { 0 };
|
|
size_t _block_size { 2048 };
|
|
|
|
void _atapi_command(Port &port)
|
|
{
|
|
Command_header header(port.command_header_addr(0));
|
|
header.atapi_command();
|
|
header.clear_byte_count();
|
|
port.execute(0);
|
|
}
|
|
|
|
void _read_sense(Port &port)
|
|
{
|
|
addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
|
|
|
|
Command_table table(port.command_table_addr(0), phys, 0x1000);
|
|
table.fis.atapi();
|
|
table.atapi_cmd.read_sense();
|
|
|
|
_atapi_command(port);
|
|
}
|
|
|
|
void _test_unit_ready(Port &port)
|
|
{
|
|
Command_table table(port.command_table_addr(0), 0, 0);
|
|
table.fis.atapi();
|
|
table.atapi_cmd.test_unit_ready();
|
|
|
|
_atapi_command(port);
|
|
}
|
|
|
|
void _read_capacity(Port &port)
|
|
{
|
|
addr_t phys = Dataspace_client(port.device_info_ds).phys_addr();
|
|
|
|
Command_table table(port.command_table_addr(0), phys, 0x1000);
|
|
table.fis.atapi();
|
|
table.fis.byte_count(~0);
|
|
|
|
table.atapi_cmd.read_capacity();
|
|
|
|
_atapi_command(port);
|
|
}
|
|
|
|
void _start_unit(Port &port)
|
|
{
|
|
Command_table table(port.command_table_addr(0), 0, 0);
|
|
table.fis.atapi();
|
|
table.atapi_cmd.start_unit();
|
|
|
|
_atapi_command(port);
|
|
}
|
|
|
|
public:
|
|
|
|
/******************************
|
|
** Ahci::Protocol interface **
|
|
******************************/
|
|
|
|
unsigned init(Port &port) override
|
|
{
|
|
port.write<Port::Cmd::Atapi>(1);
|
|
|
|
retry<Port::Polling_timeout>(
|
|
[&] {
|
|
|
|
_start_unit(port);
|
|
port.wait_for_any(port.hba.delayer(),
|
|
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
|
|
Port::Is::Dhrs::Equal(1));
|
|
port.ack_irq();
|
|
|
|
/* read sense */
|
|
_read_sense(port);
|
|
port.wait_for_any(port.hba.delayer(),
|
|
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
|
|
Port::Is::Dhrs::Equal(1));
|
|
port.ack_irq();
|
|
|
|
/* test unit ready */
|
|
_test_unit_ready(port);
|
|
port.wait_for(port.hba.delayer(), Port::Is::Dhrs::Equal(1));
|
|
port.ack_irq();
|
|
|
|
Device_fis f(port.fis_base);
|
|
/* check if devic is ready */
|
|
if (!f.read<Device_fis::Status::Device_ready>() || f.read<Device_fis::Error>())
|
|
throw Port::Polling_timeout();
|
|
|
|
_read_capacity(port);
|
|
port.wait_for_any(port.hba.delayer(),
|
|
Port::Is::Dss::Equal(1), Port::Is::Pss::Equal(1),
|
|
Port::Is::Dhrs::Equal(1));
|
|
port.ack_irq();
|
|
|
|
_block_count = host_to_big_endian(((unsigned *)port.device_info)[0]) + 1;
|
|
_block_size = host_to_big_endian(((unsigned *)port.device_info)[1]);
|
|
},
|
|
[&] {}, 3);
|
|
|
|
return 1;
|
|
}
|
|
|
|
Block::Session::Info info() const override
|
|
{
|
|
return { .block_size = _block_size,
|
|
.block_count = _block_count,
|
|
.align_log2 = 1,
|
|
.writeable = false };
|
|
}
|
|
|
|
void handle_irq(Port &port) override
|
|
{
|
|
port.ack_irq();
|
|
}
|
|
|
|
void writeable(bool) override { }
|
|
|
|
Response submit(Port &port, Block::Request const request) override
|
|
{
|
|
if (request.operation.type != Block::Operation::Type::READ ||
|
|
port.sanity_check(request) == false || port.dma_base == 0)
|
|
return Response::REJECTED;
|
|
|
|
if (_pending.operation.valid())
|
|
return Response::RETRY;
|
|
|
|
Block::Operation op = request.operation;
|
|
_pending = request;
|
|
_pending.success = false;
|
|
|
|
/* setup fis */
|
|
Command_table table(port.command_table_addr(0),
|
|
port.dma_base + request.offset,
|
|
op.count * _block_size);
|
|
table.fis.atapi();
|
|
|
|
/* setup atapi command */
|
|
table.atapi_cmd.read10(op.block_number, op.count);
|
|
table.fis.byte_count(~0);
|
|
|
|
/* set and clear write flag in command header */
|
|
Command_header header(port.command_header_addr(0));
|
|
header.write<Command_header::Bits::W>(0);
|
|
header.clear_byte_count();
|
|
|
|
/* set pending */
|
|
port.execute(0);
|
|
|
|
return Response::ACCEPTED;
|
|
}
|
|
|
|
Block::Request completed(Port &port) override
|
|
{
|
|
if (!_pending.operation.valid() || port.read<Port::Ci>())
|
|
return Block::Request();
|
|
|
|
Block::Request request = _pending;
|
|
_pending.operation.type = Block::Operation::Type::INVALID;
|
|
|
|
return request;
|
|
}
|
|
};
|
|
|
|
#endif /* _AHCI__ATAPI_PROTOCOL_H_ */
|