/** * \brief Packet stream helper * \author Sebastian Sumpf * \date 2014-12-08 */ /* * Copyright (C) 2014 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _INCLUDE__USB__PACKET_HANDLER_ #define _INCLUDE__USB__PACKET_HANDLER_ #include #include namespace Usb { class Packet_handler; } class Usb::Packet_handler { private: Usb::Connection &_connection; Signal_rpc_member _rpc_ack_avail; Signal_rpc_member _rpc_ready_submit; bool _ready_submit = true; void _packet_handler(unsigned) { if (!_ready_submit) return; while (packet_avail()) { Packet_descriptor p = _connection.source()->get_acked_packet(); if (p.completion) p.completion->complete(p); else release(p); } } void _ready_handler(unsigned) { _ready_submit = true; }; public: Packet_handler(Connection &connection, Server::Entrypoint &ep) : _connection(connection), _rpc_ack_avail(ep, *this, &Packet_handler::_packet_handler), _rpc_ready_submit(ep, *this, &Packet_handler::_ready_handler) { /* connect 'ack_avail' to our rpc member */ _connection.tx_channel()->sigh_ack_avail(_rpc_ack_avail); _connection.tx_channel()->sigh_ready_to_submit(_rpc_ready_submit); } /*************************** ** Packet stream wrapper ** ***************************/ bool packet_avail() const { return _connection.source()->ack_avail(); } void wait_for_packet() { packet_avail() ? _packet_handler(0) : Server::wait_and_dispatch_one_signal(); } Packet_descriptor alloc(size_t size) { /* is size larger than packet stream */ if (size > _connection.source()->bulk_buffer_size()) { PERR("Packet allocation of (%zu bytes) too large, buffer has %zu bytes", size, _connection.source()->bulk_buffer_size()); throw Usb::Session::Tx::Source::Packet_alloc_failed(); } if (size == 0) { /* * XXX Packets without payload are not supported by the packet * stream currently. Therefore, we use a (small) bogus * length here and depend on the USB driver to handle this * case correctly. */ size = 4; } while (true) { try { Packet_descriptor p = _connection.source()->alloc_packet(size); return p; } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { /* block until some packets are freed */ wait_for_packet(); } } } void submit(Packet_descriptor &p) { /* check if submit queue is full */ if (!_connection.source()->ready_to_submit()) { _ready_submit = false; /* wait for ready_to_submit signal */ while (!_ready_submit) Server::wait_and_dispatch_one_signal(); } _connection.source()->submit_packet(p); /* * If an acknowledgement available signal occurred in the meantime, * retrieve packets. */ if (packet_avail()) _packet_handler(0); } void *content(Packet_descriptor &p) { return _connection.source()->packet_content(p); } void release(Packet_descriptor &p) { _connection.source()->release_packet(p); } }; #endif /* _INCLUDE__USB__PACKET_HANDLER_ */