/* (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 #include extern "C" { #include #include #include #include #include #include #include #include #include } #include #include #include #include #include //namespace Voodoo { D_DEBUG_DOMAIN( Voodoo_Dispatch, "Voodoo/Dispatch", "Voodoo Dispatch" ); D_DEBUG_DOMAIN( Voodoo_Manager, "Voodoo/Manager", "Voodoo Manager" ); /**********************************************************************************************************************/ VoodooManager::VoodooManager( VoodooLink *link, VoodooContext *context ) : magic(0), is_quit(false), msg_count(0), msg_serial(0) { D_ASSERT( link != NULL ); D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); /* Store link and context */ this->link = link; this->context = context; instances.last = 0; response.current = NULL; /* Initialize all locks. */ direct_recursive_mutex_init( &instances.lock ); direct_recursive_mutex_init( &response.lock ); /* Initialize all wait conditions. */ direct_waitqueue_init( &response.wait_get ); direct_waitqueue_init( &response.wait_put ); D_MAGIC_SET( this, VoodooManager ); dispatcher = new VoodooDispatcher( this ); /* Add connection */ if ((link->code & 0x8000ffff) == 0x80008676) { D_INFO( "Voodoo/Manager: Connection mode is PACKET\n" ); connection = new VoodooConnectionPacket( this, link ); } else { D_INFO( "Voodoo/Manager: Connection mode is RAW\n" ); connection = new VoodooConnectionRaw( this, link ); // FIXME: query manager dynamically for compression instead voodoo_config->compression_min = 0; } connection->Start(); } static void instance_iterator( std::pair pair ) { D_DEBUG_AT( Voodoo_Manager, "%s( id %u, instance %p )\n", __func__, pair.first, pair.second ); pair.second->Release(); } VoodooManager::~VoodooManager() { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); if (!is_quit) quit(); connection->Stop(); /* Destroy dispatcher */ delete dispatcher; /* Remove connection */ delete connection; /* Destroy conditions. */ direct_waitqueue_deinit( &response.wait_get ); direct_waitqueue_deinit( &response.wait_put ); /* Destroy locks. */ direct_mutex_deinit( &instances.lock ); direct_mutex_deinit( &response.lock ); /* Release all remaining interfaces. */ std::for_each( instances.remote.begin(), instances.remote.end(), instance_iterator ); std::for_each( instances.local.begin(), instances.local.end(), instance_iterator ); D_MAGIC_CLEAR( this ); } /**********************************************************************************************************************/ void VoodooManager::DispatchPacket( VoodooPacket *packet ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p, packet %p )\n", __func__, this, packet ); D_MAGIC_ASSERT( this, VoodooManager ); D_ASSUME( !is_quit ); if (is_quit) return; dispatcher->PutPacket( packet ); } bool VoodooManager::DispatchReady() { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); // D_ASSUME( !is_quit ); if (is_quit) return false; return dispatcher->Ready(); } /**********************************************************************************************************************/ void VoodooManager::quit() { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); D_ASSUME( !is_quit ); if (is_quit) return; /* Have all threads quit upon this. */ is_quit = true; /* Acquire locks and wake up waiters. */ direct_mutex_lock( &response.lock ); direct_waitqueue_broadcast( &response.wait_get ); direct_waitqueue_broadcast( &response.wait_put ); direct_mutex_unlock( &response.lock ); } void VoodooManager::handle_disconnect() { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); D_DEBUG_AT( Voodoo_Manager, " -> Remote site disconnected from manager at %p!\n", this ); quit(); } void VoodooManager::handle_super( VoodooSuperMessage *super ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); DirectResult ret; const char *name; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( super != NULL ); D_ASSERT( super->header.size >= (int) sizeof(VoodooSuperMessage) ); D_ASSERT( super->header.type == VMSG_SUPER ); name = (const char *) (super + 1); D_DEBUG_AT( Voodoo_Dispatch, " -> Handling SUPER message %llu for '%s' (%d bytes).\n", (unsigned long long)super->header.serial, name, super->header.size ); VoodooInstanceID instance_id; ret = context->HandleSuper( this, name, &instance_id ); if (ret) do_respond( true, super->header.serial, ret ); else do_respond( true, super->header.serial, DR_OK, instance_id ); } typedef struct { VoodooManager *manager; VoodooInstance *instance; VoodooRequestMessage *request; } DispatchAsyncContext; void * VoodooManager::dispatch_async_thread( DirectThread *thread, void *arg ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, arg ); DirectResult ret; DispatchAsyncContext *context = (DispatchAsyncContext*) arg; VoodooManager *manager = context->manager; VoodooInstance *instance = context->instance; VoodooRequestMessage *request = context->request; ret = instance->Dispatch( manager, request ); if (ret && (request->flags & VREQ_RESPOND)) manager->do_respond( true, request->header.serial, ret ); D_FREE( context ); return NULL; } void VoodooManager::handle_request( VoodooRequestMessage *request ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); DirectResult ret; VoodooInstance *instance; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( request != NULL ); D_ASSERT( request->header.size >= (int) sizeof(VoodooRequestMessage) ); D_ASSERT( request->header.type == VMSG_REQUEST ); D_DEBUG_AT( Voodoo_Dispatch, " -> Handling REQUEST message %llu to %u::%u %s%s(%d bytes).\n", (unsigned long long)request->header.serial, request->instance, request->method, (request->flags & VREQ_RESPOND) ? "[RESPONDING] " : "", (request->flags & VREQ_ASYNC) ? "[ASYNC] " : "", request->header.size ); direct_mutex_lock( &instances.lock ); InstanceMap::iterator itr = instances.local.find( request->instance ); if (itr == instances.local.end()) { direct_mutex_unlock( &instances.lock ); D_ERROR( "Voodoo/Dispatch: " "Requested instance %u doesn't exist (anymore)!\n", request->instance ); if (request->flags & VREQ_RESPOND) do_respond( true, request->header.serial, DR_NOSUCHINSTANCE ); return; } instance = (*itr).second; if (request->flags & VREQ_ASYNC) { DirectThread *thread; DispatchAsyncContext *context; context = (DispatchAsyncContext*) D_MALLOC( sizeof(DispatchAsyncContext) + request->header.size ); if (!context) { D_WARN( "out of memory" ); direct_mutex_unlock( &instances.lock ); return; } context->manager = this; context->instance = instance; context->request = (VoodooRequestMessage*) (context + 1); direct_memcpy( context->request, request, request->header.size ); thread = direct_thread_create( DTT_DEFAULT, dispatch_async_thread, context, "Voodoo Async" ); direct_thread_detach( thread ); // FIXME: free thread? } else { ret = instance->Dispatch( this, request ); if (ret && (request->flags & VREQ_RESPOND)) do_respond( true, request->header.serial, ret ); } direct_mutex_unlock( &instances.lock ); } void VoodooManager::handle_response( VoodooResponseMessage *msg ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( msg != NULL ); D_ASSERT( msg->header.size >= (int) sizeof(VoodooResponseMessage) ); D_ASSERT( msg->header.type == VMSG_RESPONSE ); D_ASSERT( msg->request < msg_serial ); D_DEBUG_AT( Voodoo_Dispatch, " -> Handling RESPONSE message %llu (%s) with instance %u for request " "%llu (%d bytes).\n", (unsigned long long)msg->header.serial, DirectResultString( msg->result ), msg->instance, (unsigned long long)msg->request, msg->header.size ); direct_mutex_lock( &response.lock ); D_ASSERT( response.current == NULL ); response.current = msg; direct_mutex_unlock( &response.lock ); direct_waitqueue_broadcast( &response.wait_get ); direct_mutex_lock( &response.lock ); while (response.current && !is_quit) direct_waitqueue_wait( &response.wait_put, &response.lock ); direct_mutex_unlock( &response.lock ); } /**************************************************************************************************/ DirectResult VoodooManager::lock_response( VoodooMessageSerial request, VoodooResponseMessage **ret_response ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); VoodooResponseMessage *msg = NULL; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( ret_response != NULL ); D_DEBUG_AT( Voodoo_Manager, " -> Locking response to request %llu...\n", (unsigned long long)request ); direct_mutex_lock( &response.lock ); while (!is_quit) { msg = response.current; if (msg && msg->request == request) break; if (msg) D_DEBUG_AT( Voodoo_Manager, " -> ...current response is for request %llu...\n", (unsigned long long)msg->request ); D_DEBUG_AT( Voodoo_Manager, " -> ...(still) waiting for response to request %llu...\n", (unsigned long long)request ); direct_waitqueue_wait( &response.wait_get, &response.lock ); } if (is_quit) { D_ERROR( "Voodoo/Manager: Quit while waiting for response!\n" ); direct_mutex_unlock( &response.lock ); return DR_DESTROYED; } D_DEBUG_AT( Voodoo_Manager, " -> ...locked response %llu to request %llu (%d bytes).\n", (unsigned long long)msg->header.serial, (unsigned long long)request, msg->header.size ); *ret_response = msg; return DR_OK; } DirectResult VoodooManager::unlock_response( VoodooResponseMessage *msg ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( msg != NULL ); D_ASSERT( msg == response.current ); D_DEBUG_AT( Voodoo_Manager, " -> Unlocking response %llu to request %llu (%d bytes)...\n", (unsigned long long)msg->header.serial, (unsigned long long)msg->request, msg->header.size ); response.current = NULL; direct_mutex_unlock( &response.lock ); direct_waitqueue_broadcast( &response.wait_put ); return DR_OK; } DirectResult VoodooManager::do_super( const char *name, VoodooInstanceID *ret_instance ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); DirectResult ret; int len; int size; VoodooPacket *packet; VoodooMessageSerial serial; VoodooSuperMessage *msg; VoodooResponseMessage *response; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( name != NULL ); D_ASSERT( ret_instance != NULL ); if (is_quit) { D_DEBUG_AT( Voodoo_Manager, " -> QUIT!\n" ); return DR_IO; } /* Calculate the total message size. */ len = strlen( name ) + 1; size = sizeof(VoodooSuperMessage) + len; /* Lock the output buffer for direct writing. */ packet = connection->GetPacket( size ); if (!packet) return DR_FAILURE; msg = (VoodooSuperMessage*) packet->data_raw(); serial = msg_serial++; /* Fill message header. */ msg->header.size = size; msg->header.serial = serial; msg->header.type = VMSG_SUPER; /* Append the name of the super interface to create. */ direct_memcpy( msg + 1, name, len ); D_DEBUG_AT( Voodoo_Manager, " -> Sending SUPER message %llu for '%s' (%d bytes).\n", (unsigned long long)serial, name, size ); /* Unlock the output buffer. */ connection->PutPacket( packet, true ); /* Wait for and lock the response buffer. */ ret = lock_response( serial, &response ); if (ret) { D_ERROR( "Voodoo/Manager: " "Waiting for the response failed (%s)!\n", DirectResultString( ret ) ); return ret; } D_DEBUG_AT( Voodoo_Manager, " -> Got response %llu (%s) with instance %u for request %llu " "(%d bytes).\n", (unsigned long long)response->header.serial, DirectResultString( ret ), response->instance, (unsigned long long)response->request, response->header.size ); ret = response->result; if (ret) { D_ERROR( "Voodoo/Manager: Could not create remote super interface '%s' (%s)!\n", name, DirectResultString( ret ) ); unlock_response( response ); return ret; } D_INFO( "Voodoo/Manager: Created remote super interface '%s'.\n", name ); /* Return the new instance ID. */ *ret_instance = response->instance; /* Unlock the response buffer. */ unlock_response( response ); return DR_OK; } void VoodooManager::write_blocks( void *dst, const VoodooMessageBlock *blocks, size_t num ) { size_t i; u32 *d32 = (u32*) dst; for (i=0; i> 2); } /* Write terminator. */ d32[0] = VMBT_NONE; } DirectResult VoodooManager::do_request( VoodooInstanceID instance, VoodooMethodID method, VoodooRequestFlags flags, VoodooResponseMessage **ret_response, VoodooMessageBlock *blocks, size_t num_blocks, size_t data_size ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); DirectResult ret; size_t size; VoodooPacket *packet; VoodooMessageSerial serial; VoodooRequestMessage *msg; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( instance != VOODOO_INSTANCE_NONE ); D_ASSERT( ret_response != NULL || !(flags & VREQ_RESPOND) ); D_ASSUME( (flags & (VREQ_RESPOND | VREQ_QUEUE)) != (VREQ_RESPOND | VREQ_QUEUE) ); D_DEBUG_AT( Voodoo_Manager, " -> Instance %u, method %u, flags 0x%08x...\n", instance, method, flags ); if (is_quit) { D_DEBUG_AT( Voodoo_Manager, " -> QUIT!\n" ); return DR_IO; } /* Calculate the total message size. */ size = sizeof(VoodooRequestMessage) + data_size; D_DEBUG_AT( Voodoo_Manager, " -> complete message size: %d\n", size ); /* Lock the output buffer for direct writing. */ packet = connection->GetPacket( size ); if (!packet) return DR_FAILURE; msg = (VoodooRequestMessage*) packet->data_raw(); serial = msg_serial++; /* Fill message header. */ msg->header.size = size; msg->header.serial = serial; msg->header.type = VMSG_REQUEST; /* Fill message body. */ msg->instance = instance; msg->method = method; msg->flags = flags; /* Append custom data. */ write_blocks( msg + 1, blocks, num_blocks ); D_DEBUG_AT( Voodoo_Manager, " -> Sending REQUEST message %llu to %u::%u %s(%d bytes).\n", (unsigned long long)serial, instance, method, (flags & VREQ_RESPOND) ? "[RESPONDING] " : "", size ); /* Unlock the output buffer. */ connection->PutPacket( packet, !(flags & VREQ_QUEUE) ); /* Wait for and lock the response buffer. */ if (flags & VREQ_RESPOND) { VoodooResponseMessage *response; ret = lock_response( serial, &response ); if (ret) { D_ERROR( "Voodoo/Manager: " "Waiting for the response failed (%s)!\n", DirectResultString( ret ) ); return ret; } D_DEBUG_AT( Voodoo_Manager, " -> Got response %llu (%s) with instance %u for request %llu " "(%d bytes).\n", (unsigned long long)response->header.serial, DirectResultString( response->result ), response->instance, (unsigned long long)response->request, response->header.size ); *ret_response = response; } return DR_OK; } DirectResult VoodooManager::next_response( VoodooResponseMessage *response, VoodooResponseMessage **ret_response ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); DirectResult ret; VoodooMessageSerial serial; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( response != NULL ); serial = response->request; /* Unlock the response buffer. */ unlock_response( response ); ret = lock_response( serial, &response ); if (ret) { D_ERROR( "Voodoo/Manager: " "Waiting for the response failed (%s)!\n", DirectResultString( ret ) ); return ret; } D_DEBUG_AT( Voodoo_Manager, " -> Got response %llu (%s) with instance %u for request %llu " "(%d bytes).\n", (unsigned long long)response->header.serial, DirectResultString( response->result ), response->instance, (unsigned long long)response->request, response->header.size ); *ret_response = response; return DR_OK; } DirectResult VoodooManager::finish_request( VoodooResponseMessage *response ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( response != NULL ); /* Unlock the response buffer. */ return unlock_response( response ); } DirectResult VoodooManager::do_respond( bool flush, VoodooMessageSerial request, DirectResult result, VoodooInstanceID instance, VoodooMessageBlock *blocks, size_t num_blocks, size_t data_size ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); size_t size; VoodooPacket *packet; VoodooMessageSerial serial; VoodooResponseMessage *msg; D_MAGIC_ASSERT( this, VoodooManager ); D_DEBUG_AT( Voodoo_Manager, " -> Request %llu, result %d, instance %u...\n", (unsigned long long)request, result, instance ); if (is_quit) { D_DEBUG_AT( Voodoo_Manager, " -> QUIT!\n" ); return DR_IO; } /* Calculate the total message size. */ size = sizeof(VoodooResponseMessage) + data_size; D_DEBUG_AT( Voodoo_Manager, " -> complete message size: %d\n", size ); /* Lock the output buffer for direct writing. */ packet = connection->GetPacket( size ); if (!packet) return DR_FAILURE; msg = (VoodooResponseMessage*) packet->data_raw(); serial = msg_serial++; /* Fill message header. */ msg->header.size = size; msg->header.serial = serial; msg->header.type = VMSG_RESPONSE; /* Fill message body. */ msg->request = request; msg->result = result; msg->instance = instance; /* Append custom data. */ write_blocks( msg + 1, blocks, num_blocks ); D_DEBUG_AT( Voodoo_Manager, " -> Sending RESPONSE message %llu (%s) with instance %u for request %llu (%d bytes).\n", (unsigned long long)serial, DirectResultString( result ), instance, (unsigned long long)request, size ); /* Unlock the output buffer. */ connection->PutPacket( packet, flush ); return DR_OK; } DirectResult VoodooManager::register_local( VoodooInstance *instance, VoodooInstanceID *ret_instance ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); VoodooInstanceID instance_id; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( instance != NULL ); D_ASSERT( ret_instance != NULL ); instance->AddRef(); direct_mutex_lock( &instances.lock ); instance_id = ++instances.last; instances.local[instance_id] = instance; direct_mutex_unlock( &instances.lock ); D_DEBUG_AT( Voodoo_Manager, " -> Added local instance %u (%p)\n", instance_id, instance ); *ret_instance = instance_id; return DR_OK; } DirectResult VoodooManager::unregister_local( VoodooInstanceID instance_id ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); VoodooInstance *instance; D_MAGIC_ASSERT( this, VoodooManager ); direct_mutex_lock( &instances.lock ); InstanceMap::iterator itr = instances.local.find( instance_id ); if (itr == instances.local.end()) { direct_mutex_unlock( &instances.lock ); return DR_NOSUCHINSTANCE; } instance = (*itr).second; instances.local.erase( itr ); direct_mutex_unlock( &instances.lock ); instance->Release(); return DR_OK; } DirectResult VoodooManager::lookup_local( VoodooInstanceID instance_id, VoodooInstance **ret_instance ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); VoodooInstance *instance; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( instance_id != VOODOO_INSTANCE_NONE ); D_ASSERT( ret_instance != NULL ); direct_mutex_lock( &instances.lock ); InstanceMap::iterator itr = instances.local.find( instance_id ); direct_mutex_unlock( &instances.lock ); if (itr == instances.local.end()) return DR_NOSUCHINSTANCE; instance = (*itr).second; // FIXME: addref? *ret_instance = instance; return DR_OK; } DirectResult VoodooManager::register_remote( VoodooInstance *instance, VoodooInstanceID instance_id ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( instance != NULL ); D_ASSERT( instance_id != VOODOO_INSTANCE_NONE ); instance->AddRef(); direct_mutex_lock( &instances.lock ); instances.remote[instance_id] = instance; direct_mutex_unlock( &instances.lock ); D_DEBUG_AT( Voodoo_Manager, " -> Added remote instance %u (%p)\n", instance_id, instance ); return DR_OK; } DirectResult VoodooManager::unregister_remote( VoodooInstanceID instance_id ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); VoodooInstance *instance; D_MAGIC_ASSERT( this, VoodooManager ); direct_mutex_lock( &instances.lock ); InstanceMap::iterator itr = instances.remote.find( instance_id ); if (itr == instances.remote.end()) { direct_mutex_unlock( &instances.lock ); return DR_NOSUCHINSTANCE; } instance = (*itr).second; instances.remote.erase( itr ); direct_mutex_unlock( &instances.lock ); instance->Release(); return DR_OK; } DirectResult VoodooManager::lookup_remote( VoodooInstanceID instance_id, VoodooInstance **ret_instance ) { D_DEBUG_AT( Voodoo_Manager, "VoodooManager::%s( %p )\n", __func__, this ); VoodooInstance *instance; D_MAGIC_ASSERT( this, VoodooManager ); D_ASSERT( instance_id != VOODOO_INSTANCE_NONE ); D_ASSERT( ret_instance != NULL ); direct_mutex_lock( &instances.lock ); InstanceMap::iterator itr = instances.remote.find( instance_id ); direct_mutex_unlock( &instances.lock ); if (itr == instances.remote.end()) return DR_NOSUCHINSTANCE; instance = (*itr).second; // FIXME: addref? *ret_instance = instance; return DR_OK; } //}