/* * \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 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(1); retry( [&] { _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() || f.read()) 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(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()) return Block::Request(); Block::Request request = _pending; _pending.operation.type = Block::Operation::Type::INVALID; return request; } }; #endif /* _AHCI__ATAPI_PROTOCOL_H_ */