/* (c) Copyright 2001-2011 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. Written by Denis Oliver Kropp , Andreas Hundt , Sven Neumann , Ville Syrjälä and Claudio Ciccani . This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //#define DIRECT_ENABLE_DEBUG #include #ifdef VOODOO_CONNECTION_RAW_DUMP #include #include #include #include #include #include #include #endif extern "C" { #include #include #include #include #include #include #include #include #include } #include #include #include #include //namespace Voodoo { D_DEBUG_DOMAIN( Voodoo_Connection, "Voodoo/Connection", "Voodoo Connection" ); D_DEBUG_DOMAIN( Voodoo_Input, "Voodoo/Input", "Voodoo Input" ); D_DEBUG_DOMAIN( Voodoo_Output, "Voodoo/Output", "Voodoo Output" ); /**********************************************************************************************************************/ VoodooConnectionRaw::VoodooConnectionRaw( VoodooManager *manager, VoodooLink *link ) : VoodooConnectionLink( manager, link ), stop( false ), closed( false ) { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionRaw::%s( %p )\n", __func__, this ); if (link->code) { input.end = 4; memcpy( input.buffer, &link->code, sizeof(u32) ); } } VoodooConnectionRaw::~VoodooConnectionRaw() { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionRaw::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooConnection ); } void VoodooConnectionRaw::Start() { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionRaw::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooConnection ); io = direct_thread_create( DTT_DEFAULT, io_loop_main, this, "Voodoo IO" ); } void VoodooConnectionRaw::Stop() { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionRaw::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooConnection ); direct_mutex_lock( &output.lock ); while (!closed && output.packets) { D_DEBUG_AT( Voodoo_Connection, " -> waiting for output packets to be sent...\n" ); direct_waitqueue_wait( &output.wait, &output.lock ); } direct_mutex_unlock( &output.lock ); stop = true; link->WakeUp( link ); /* Wait for manager threads exiting. */ direct_thread_join( io ); direct_thread_destroy( io ); VoodooConnectionLink::Stop(); } /**********************************************************************************************************************/ void * VoodooConnectionRaw::io_loop() { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionRaw::%s( %p )\n", __func__, this ); #ifdef VOODOO_CONNECTION_RAW_DUMP int dump_fd = open("voodoo_write.raw", O_TRUNC|O_CREAT|O_WRONLY, 0660 ); int dump_read_fd = open("voodoo_read.raw", O_TRUNC|O_CREAT|O_WRONLY, 0660 ); #endif while (!stop) { D_MAGIC_ASSERT( this, VoodooConnection ); if (input.start == input.max) { input.start = 0; input.end = 0; input.last = 0; input.max = VOODOO_CONNECTION_LINK_INPUT_BUF_MAX; } if (!stop) { DirectResult ret; VoodooChunk chunks[2]; VoodooChunk *chunk_read = NULL; VoodooChunk *chunk_write = NULL; size_t last = input.last; VoodooPacket *packet = NULL; std::vector chunks_write; std::vector chunks_read; if (!output.sending) { direct_mutex_lock( &output.lock ); if (output.packets) { VoodooPacket *packet = (VoodooPacket*) output.packets; D_ASSERT( packet->sending ); output.sending = packet; output.sent = 0; } direct_mutex_unlock( &output.lock ); } if (output.sending) { packet = output.sending; D_ASSERT( packet->sending ); chunk_write = &chunks[1]; chunk_write->ptr = (char*) packet->data_start() + output.sent; chunk_write->length = VOODOO_MSG_ALIGN(packet->size()) - output.sent; chunk_write->done = 0; chunks_write.push_back( chunks[1] ); chunk_write = chunks_write.data(); } if (input.end < input.max && manager->DispatchReady()) { chunk_read = &chunks[0]; chunk_read->ptr = input.buffer + input.end; chunk_read->length = input.max - input.end; chunk_read->done = 0; chunks_read.push_back( chunks[0] ); chunk_read = chunks_read.data(); } ret = link->SendReceive( link, chunks_write.data(), chunks_write.size(), chunks_read.data(), chunks_read.size() ); switch (ret) { case DR_OK: if (chunk_write && chunk_write->done) { D_DEBUG_AT( Voodoo_Output, " -> Sent "_ZD"/"_ZD" bytes...\n", chunk_write->done, chunk_write->length ); #ifdef VOODOO_CONNECTION_RAW_DUMP write( dump_fd, chunk_write->ptr, chunk_write->done ); #endif output.sent += chunk_write->done; if (output.sent == VOODOO_MSG_ALIGN(packet->size())) { output.sending = NULL; direct_mutex_lock( &output.lock ); packet->sending = false; direct_list_remove( &output.packets, &packet->link ); direct_mutex_unlock( &output.lock ); direct_waitqueue_broadcast( &output.wait ); } } break; case DR_TIMEOUT: //D_DEBUG_AT( Voodoo_Connection, " -> timeout\n" ); break; case DR_INTERRUPTED: D_DEBUG_AT( Voodoo_Connection, " -> interrupted\n" ); break; default: if (ret == DR_IO) D_DEBUG_AT( Voodoo_Connection, " -> Connection closed!\n" ); else { D_DERROR( ret, "Voodoo/ConnectionRaw: Could not receive data!\n" ); exit(0); } closed = true; manager->handle_disconnect(); return NULL; } if (chunk_read && chunk_read->done) { D_DEBUG_AT( Voodoo_Input, " -> Received "_ZD" bytes...\n", chunk_read->done ); #ifdef VOODOO_CONNECTION_RAW_DUMP write( dump_read_fd, chunk_read->ptr, chunk_read->done ); #endif input.end += (size_t) chunk_read->done; do { VoodooMessageHeader *header; size_t aligned; D_DEBUG_AT( Voodoo_Input, " { LAST "_ZD", INPUT LAST "_ZD" }\n", last, input.last ); if (input.end - last < 4) { D_DEBUG_AT( Voodoo_Input, " -> ...only "_ZU" bytes left\n", input.end - last ); break; } /* Get the message header. */ header = (VoodooMessageHeader *)(input.buffer + last); aligned = VOODOO_MSG_ALIGN( header->size ); D_DEBUG_AT( Voodoo_Input, " -> Next message has %d ("_ZD") bytes and is of type %d...\n", header->size, aligned, header->type ); D_ASSERT( header->size >= (int) sizeof(VoodooMessageHeader) ); D_ASSERT( header->size <= MAX_MSG_SIZE ); if (aligned > input.end - last) { D_DEBUG_AT( Voodoo_Input, " -> ...fetching tail of message.\n" ); /* Extend the buffer if the message doesn't fit into the default boundary. */ if (aligned > input.max - last) input.max = last + aligned; break; } last += aligned; } while (last < input.end); if (last != input.last) { input.last = last; D_DEBUG_AT( Voodoo_Input, " { START "_ZD", LAST "_ZD", END "_ZD", MAX "_ZD" }\n", input.start, input.last, input.end, input.max ); // FIXME: don't copy, but read into packet directly, maybe call manager->GetPacket() at the top of this loop VoodooPacket *p = VoodooPacket::Copy( input.last - input.start, VPHF_NONE, input.last - input.start, input.buffer + input.start ); manager->DispatchPacket( p ); input.start = input.last; } } } } return NULL; } /**********************************************************************************************************************/ void * VoodooConnectionRaw::io_loop_main( DirectThread *thread, void *arg ) { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionRaw::%s( %p, thread %p )\n", __func__, arg, thread ); VoodooConnectionRaw *connection = (VoodooConnectionRaw*) arg; return connection->io_loop(); }