/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mas_api_internal.h"
#include "mas_getset.h"

#define DEFAULT_PACKAGE_SIZE 4096

/** LOCAL PROTOTYPES *****************************************************/


/** EXPORTED FUNCTIONS ***************************************************/

mas_channel_t
_copy_channel_or_display( mas_channel_t channel )
{
    mas_channel_t oc;
    
    /* if there's no channel specified, assume the display */
    if ( channel == 0 )
    {
        mas_get_display_control_channel( &oc );
    }
    else /* it's the specified channel */
    {
        _alloc_channel( &oc );
        _copy_channel( oc, channel );
    }

    return oc;
}

int32
mas_asm_instantiate_device_on_channel( char* name, void* predicate, int32 predicate_len, mas_device_t* device, mas_channel_t channel ) 
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 err, di;
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_instantiate_device";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_string( &ppkg, "d", name );
    if ( predicate_len > 0 )
        masc_pushk_payload( &ppkg, "p", predicate, predicate_len );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, channel );
    masc_strike_package( &ppkg );
    
    /* wait for the reply, then form into package */
    err = mas_recv_package( channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the device instance */
    *device = 0;
    masc_pull_int32( &rpkg, &di );
    masc_strike_package( &rpkg );
    if ( di < 0 ) return di;
    
    _alloc_device( device );
    (*device)->device_instance = di;
    (*device)->control_channel = _copy_channel_or_display( channel );
    
    return 0;
}

int32
mas_asm_instantiate_device( char* name, void* predicate, int32 predicate_len,
                            mas_device_t* device ) 
{
    return mas_asm_instantiate_device_on_channel( name, predicate, predicate_len,
                                                  device, 0 );
}


int32
mas_asm_terminate_device_instance( mas_device_t device, int32 secs_to_live )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 err;
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_terminate_device_instance";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "di", device->device_instance );
    masc_pushk_int32( &ppkg, "stl", secs_to_live );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, device->control_channel );
    masc_strike_package( &ppkg );

    /* wait for the reply, then form into package */
    err = mas_recv_package( device->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );

    return err;
}


int32
mas_asm_disconnect_port( mas_port_t port )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 err;
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_disconnect_port";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "pn", port->portnum );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, port->control_channel );
    masc_strike_package( &ppkg );

    /* wait for the reply, then form into package */
    err = mas_recv_package( port->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );

    return err;
}

int32
mas_asm_connect_source_sink( mas_port_t source, mas_port_t sink, 
			     struct  mas_data_characteristic* dc )
{
    struct mas_package  ppkg;
    struct mas_package  dcpkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf1[DEFAULT_PACKAGE_SIZE];
    char pbuf2[DEFAULT_PACKAGE_SIZE];
    int32 err;
    
    if ( (source->portnum == 0) || (sink->portnum == 0) )
        return mas_error(MERR_INVALID);
    
    /* can't support connecting across the net device yet. */
    if ( source->control_channel->id != sink->control_channel->id )
        return mas_error(MERR_NOSUPP);
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_connect_source_sink";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf1, sizeof pbuf1, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "src", source->portnum );
    masc_pushk_int32( &ppkg, "snk", sink->portnum );
    /* package the dc first, then stuff as package */
    masc_setup_package( &dcpkg, pbuf2, sizeof pbuf2, MASC_PACKAGE_STATIC );
    masc_pack_dc( &dcpkg, dc );
    masc_finalize_package( &dcpkg );

    masc_push_package( &ppkg, &dcpkg );
    masc_finalize_package( &ppkg );
    
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, source->control_channel );
    masc_strike_package( &ppkg );
    masc_strike_package( &dcpkg );

    /* wait for the reply, then form into package */
    err = mas_recv_package( source->control_channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );

    return err;
}

int32
mas_asm_connect_ports( mas_port_t source, mas_port_t sink )
{
    struct mas_event   event;
    struct mas_package ppkg;
    struct mas_package rpkg;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    mas_channel_t local;
    mas_device_t net;
    mas_port_t   l_source, l_sink; /* local */
    mas_port_t   d_source, d_sink; /* display-side */
    struct mas_data_characteristic* dc;
    int32 err;

    if ( (source->portnum == 0) || (sink->portnum == 0) )
        return mas_error(MERR_INVALID);

    /* if source and sink are on the same server */
    if ( source->control_channel->id == sink->control_channel->id )
    {
        /* make an event */
        masc_setup_event( &event );
        event.action_name = "mas_asm_connect_ports";

        /* stuff the predicate package */
        masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
        masc_pushk_int32( &ppkg, "src", source->portnum );
        masc_pushk_int32( &ppkg, "snk", sink->portnum );
        masc_finalize_package( &ppkg );
        event.predicate = ppkg.contents;
        event.predicate_length = ppkg.size;
    
        /* package & send event to server */
        mas_send_event( &event, source->control_channel );
        masc_strike_package( &ppkg );
        
        /* wait for the reply, then form into package */
        err = mas_recv_package( source->control_channel, &rpkg );
        if ( err < 0 ) return err;
        
        /* grab the error code */
        masc_pull_int32( &rpkg, &err );
        masc_strike_package( &rpkg );
        return err;
    }
    else /* source and sink are on different servers - connect across the network */
    {
        err = mas_get_local_control_channel( &local );
        if ( err < 0 ) return err;
        
        err = mas_asm_get_device_by_name_on_channel( "net", &net, local );
        if ( err < 0 ) return err;

        /* if the source port is accessed through the same control
           channel as the net device, then the sink port must be on
           the display side. */
        if ( source->control_channel->id == net->control_channel->id )
        {
            /* source == local, sink == display */
            err = mas_net_connect_data( net, sink->control_channel, "", &l_source, &l_sink, &d_source, &d_sink );
            if ( err < 0 ) return err;

            /* TODO: transfer type info */

            /* call ourselves to connect the ports that ARE on the
               same server now */
            err = mas_asm_connect_ports( source, l_sink );
            if ( err < 0 ) return err;

            err = mas_asm_get_dc( l_sink, &dc );
            if ( err < 0 ) return err;
            
            err = mas_asm_connect_source_sink( d_source, sink, dc );
            if ( err < 0 ) return err;
        }
        else /* the source port is on the display side */
        {
            /* source == display, sink == local */
            err = mas_net_connect_data( net, source->control_channel, "", &l_source, &l_sink, &d_source, &d_sink );
            if ( err < 0 ) return err;

            /* TODO: transfer type info */

            /* call ourselves to connect the ports that ARE on the
               same server now */
            err = mas_asm_connect_ports( source, d_sink );
            if ( err < 0 ) return err;

            err = mas_asm_get_dc( d_sink, &dc );
            if ( err < 0 ) return err;
            
            err = mas_asm_connect_source_sink( l_source, sink, dc );
            if ( err < 0 ) return err;
        }
        
        masc_strike_dc( dc );
        masc_rtfree( dc );
            
        _free_device( &net );
        _free_channel( &local );
        _free_port( &l_source );
        _free_port( &l_sink );
        _free_port( &d_source );
        _free_port( &d_sink );
    }

    return 0;
}

int32
mas_asm_connect_inline( mas_port_t srca, mas_port_t snkb, struct  mas_data_characteristic* ab_dc, mas_port_t srcc, mas_port_t snkd, struct  mas_data_characteristic* cd_dc, int config )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_package  dcpkg;
    struct mas_event    event;
    char pbuf1[DEFAULT_PACKAGE_SIZE];
    char pbuf2[DEFAULT_PACKAGE_SIZE];
    int32 err;

    if ( srca->portnum == 0 || snkb->portnum == 0 || srcc->portnum == 0 || snkd->portnum == 0)
        return mas_error(MERR_INVALID);
    
    /* can't support connecting across the net device yet. */
    if ( srca->control_channel->id != snkd->control_channel->id )
        return mas_error(MERR_NOSUPP);
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_connect_inline";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf1, sizeof pbuf1, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "srca", srca->portnum );
    masc_pushk_int32( &ppkg, "snkb", snkb->portnum );
    masc_pushk_int32( &ppkg, "srcc", srcc->portnum );
    masc_pushk_int32( &ppkg, "snkd", snkd->portnum );
    if ( ab_dc != NULL )
    {
        masc_setup_package( &dcpkg, pbuf2, sizeof pbuf2, MASC_PACKAGE_STATIC );
        masc_pack_dc( &dcpkg, ab_dc );
        masc_pushk_package( &ppkg, "ab_dc", &dcpkg );
        masc_strike_package( &dcpkg );
    }
    
    if ( cd_dc != NULL )
    {
        masc_setup_package( &dcpkg, pbuf2, sizeof pbuf2, MASC_PACKAGE_STATIC );
        masc_pack_dc( &dcpkg, cd_dc );
        masc_pushk_package( &ppkg, "cd_dc", &dcpkg );
        masc_strike_package( &dcpkg );
    }

    masc_pushk_int8( &ppkg, "config", (int8)config );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;
    
    /* package & send event to server */
    mas_send_event( &event, srca->control_channel );
    masc_strike_package( &ppkg );
    
    /* wait for the reply, then form into package */
    err = mas_recv_package( srca->control_channel, &rpkg );
    if ( err < 0 ) return err;
    
    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );
    return err;
}

int32
mas_asm_disconnect_inline( mas_port_t srca, mas_port_t snkb, mas_port_t srcc, mas_port_t snkd, struct  mas_data_characteristic* dc, int config )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_package  dcpkg;
    struct mas_event    event;
    char pbuf1[DEFAULT_PACKAGE_SIZE];
    char pbuf2[DEFAULT_PACKAGE_SIZE];
    int32 err;

    if ( srca->portnum == 0 || snkb->portnum == 0 || srcc->portnum == 0 || snkd->portnum == 0)
        return mas_error(MERR_INVALID);
    
    /* can't support disconnecting across the net device yet. */
    if ( srca->control_channel->id != snkd->control_channel->id )
        return mas_error(MERR_NOSUPP);
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_disconnect_inline";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf1, sizeof pbuf1, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "srca", srca->portnum );
    masc_pushk_int32( &ppkg, "snkb", snkb->portnum );
    masc_pushk_int32( &ppkg, "srcc", srcc->portnum );
    masc_pushk_int32( &ppkg, "snkd", snkd->portnum );
    if ( dc != NULL )
    {
        masc_setup_package( &dcpkg, pbuf2, sizeof pbuf2, MASC_PACKAGE_STATIC );
        masc_pack_dc( &dcpkg, dc );
        masc_pushk_package( &ppkg, "dc", &dcpkg );
        masc_strike_package( &dcpkg );
    }
    masc_pushk_int8( &ppkg, "config", (int8)config );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;
    
    /* package & send event to server */
    mas_send_event( &event, srca->control_channel );
    masc_strike_package( &ppkg );
    
    /* wait for the reply, then form into package */
    err = mas_recv_package( srca->control_channel, &rpkg );
    if ( err < 0 ) return err;
    
    /* grab the error code */
    masc_pull_int32( &rpkg, &err );
    masc_strike_package( &rpkg );
    return err;
}

/* These have a race condition: with replicating ports, someone else
   could grab the port between the time we grab it here and we connect
   to it.  See mas_asm_connect_named_ports action for how to handle,
   but I don't have time right now to do this for real. */

int32
mas_asm_connect_devices( mas_device_t src, mas_device_t snk, char* srcp_name, char* sinkp_name )
{
    mas_port_t src_src, snk_snk;
    int32 err;

    err = mas_asm_get_port_by_name( src, srcp_name, &src_src );
    if ( err < 0 ) return err;
    err = mas_asm_get_port_by_name( snk, sinkp_name, &snk_snk );
    if ( err < 0 ) return err;
    
    return mas_asm_connect_ports( src_src, snk_snk );
}


int32
mas_asm_connect_devices_dc( mas_device_t src, mas_device_t snk, char* srcp_name, char* sinkp_name, struct mas_data_characteristic* dc )
{
    mas_port_t src_src, snk_snk;
    int32 err;

    err = mas_asm_get_port_by_name( src, srcp_name, &src_src );
    if ( err < 0 ) return err;
    err = mas_asm_get_port_by_name( snk, sinkp_name, &snk_snk );
    if ( err < 0 ) return err;
    
    return mas_asm_connect_source_sink( src_src, snk_snk, dc );
}


int32
mas_asm_get_port_by_name( mas_device_t device, char* name, mas_port_t* port )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    mas_channel_t       channel = 0;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 pn, err;
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_get_port_by_name";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    if ( device == 0 ) masc_pushk_int32( &ppkg, "di", MAS_ALL_DEVICES );
    else masc_pushk_int32( &ppkg, "di", device->device_instance );
    masc_pushk_string( &ppkg, "n", name );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    if ( device != NULL )
        channel = device->control_channel;
    mas_send_event( &event, channel );
    masc_strike_package( &ppkg );

    /* wait for the reply, then form into package */
    err = mas_recv_package( channel, &rpkg );
    if ( err < 0 ) return err;

    /* grab the error code */
    masc_pull_int32( &rpkg, &pn );
    masc_strike_package( &rpkg );
    if ( pn < 0 ) return pn;
    
    err = _alloc_port( port );
    if (err < 0) return err;
    (*port)->portnum = pn;
    (*port)->control_channel = _copy_channel_or_display( channel );
    
    return 0;
}

int32
mas_asm_get_device_by_name_on_channel( char* name, mas_device_t* device, mas_channel_t channel )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 err;
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_get_device_by_name";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_string( &ppkg, "n", name );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, channel );
    masc_strike_package( &ppkg );

    /* wait for the reply, then form into package */
    err = mas_recv_package( channel, &rpkg );
    if ( err < 0 ) return err;

    err = _alloc_device( device );
    if ( err < 0 ) return err;
    masc_pull_int32( &rpkg, &((*device)->device_instance) );
    masc_strike_package( &rpkg );
    (*device)->control_channel = _copy_channel_or_display( channel );

    return 0;
}

int32
mas_asm_get_device_by_name( char* name,  mas_device_t* device )
{
    return mas_asm_get_device_by_name_on_channel( name, device, 0 );
}

int32
mas_dev_show_state( mas_device_t device ) 
{
    struct mas_event    event;
    int32 err;
    
    masc_setup_event( &event );
    event.action_name = "mas_dev_show_state";
    event.device_instance = device->device_instance;

    err = mas_send_event( &event, device->control_channel );
    if ( err < 0) return err;

    return 0;
}

int32
mas_asm_get_dc( mas_port_t port, struct mas_data_characteristic** retval_dc )
{
    struct mas_package  ppkg;
    struct mas_package  rpkg;
    struct mas_event    event;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 err;
    
    masc_setup_event( &event );
    event.action_name = "mas_asm_get_dc";
    
    /* stuff the predicate package */
    masc_setup_package( &ppkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &ppkg, "pn", port->portnum );
    masc_finalize_package( &ppkg );
    event.predicate = ppkg.contents;
    event.predicate_length = ppkg.size;

    /* package & send event to server */
    mas_send_event( &event, port->control_channel );
    masc_strike_package( &ppkg );

    *retval_dc = NULL;
    /* wait for the reply, then form into package */
    err = mas_recv_package( port->control_channel, &rpkg );
    if ( err < 0 ) return err;

    if ( masc_test_key( &rpkg, "err" ) == 0 )
    {
        masc_pullk_int32( &rpkg, "err", &err );
        goto done;
    }

    *retval_dc = MAS_NEW( *retval_dc );
    err = masc_unpack_dc( &rpkg, *retval_dc );

 done:
    masc_strike_package( &rpkg );
    return err;
}

static int32
_create_stub_device( mas_device_t* device, mas_channel_t channel )
{
    int32 err;
    
    err = _alloc_device( device );
    if (err < 0) return err;

    (*device)->control_channel = _copy_channel_or_display( channel );
    return 0;
}

int32
mas_get_asm_device_on_channel( mas_device_t* device, mas_channel_t channel )
{
    int32 err;

    err = _create_stub_device( device, channel );
    if ( err < 0 ) return err;
    
    (*device)->device_instance = MAS_ASM_INSTANCE;
    return 0;
}

int32
mas_get_sch_device_on_channel( mas_device_t* device, mas_channel_t channel )
{
    int32 err;
    
    err = _create_stub_device( device, channel );
    if ( err < 0 ) return err;
    
    
    (*device)->device_instance = MAS_SCH_INSTANCE;
    return err;
}

int32
mas_get_mc_device_on_channel( mas_device_t* device, mas_channel_t channel )
{
    int32 err;
    
    err = _create_stub_device( device, channel );
    if ( err < 0 ) return err;
    
    
    (*device)->device_instance = MAS_MC_INSTANCE;
    return err;
}

int32
mas_get_asm_device( mas_device_t* device )
{
    return mas_get_asm_device_on_channel( device, 0 );
}

int32
mas_get_sch_device( mas_device_t* device )
{
    return mas_get_sch_device_on_channel( device, 0 );
}

int32
mas_get_mc_device( mas_device_t* device )
{
    return mas_get_mc_device_on_channel( device, 0 );
}

int32
mas_asm_get_connport( mas_port_t port, mas_port_t* connport_ptr )
{
    int32 err;
    mas_device_t asm_dev;
    struct mas_package arg;
    struct mas_package nugget;
    char pbuf[DEFAULT_PACKAGE_SIZE];
    int32 portnum;
    
    err = mas_get_asm_device_on_channel( &asm_dev, port->control_channel );
    if ( err < 0 ) return err;
    
    masc_setup_package( &arg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC );
    masc_pushk_int32( &arg, "pn", port->portnum );
    masc_finalize_package( &arg );
    err = mas_get( asm_dev, "connport", &arg, &nugget );
    masc_strike_package( &arg );

    if ( err < 0 ) return err;

    if ( masc_test_key( &nugget, "err" ) == 0 )
    {
        masc_pullk_int32( &nugget, "err", &err );
        goto done;
    }

    masc_pullk_int32( &nugget, "connport", &portnum);

    _alloc_port( connport_ptr );
    (*connport_ptr)->portnum = portnum;
    err = _alloc_channel( &((*connport_ptr)->control_channel) );
    _copy_channel( (*connport_ptr)->control_channel, port->control_channel );

 done:
    masc_strike_package( &nugget );
    return err;
}

int32
mas_get_portnum( mas_port_t port )
{
    return port->portnum;
}

int32
mas_get_port( int32 portnum, mas_channel_t channel, mas_port_t *port )
{
    _alloc_port( port );
    (*port)->portnum = portnum;

    if ( channel == NULL )
    {
        mas_get_display_control_channel( &((*port)->control_channel) );
    }
    else
    {
        _alloc_channel( &((*port)->control_channel) );
        _copy_channel( (*port)->control_channel, channel );
    }

    return 0;
}

int32
mas_get_device( int32 di, mas_channel_t channel, mas_device_t *device )
{
    _alloc_device( device );
    (*device)->device_instance = di;

    if ( channel == NULL )
    {
        mas_get_display_control_channel( &((*device)->control_channel) );
    }
    else
    {
        _alloc_channel( &((*device)->control_channel) );
        _copy_channel( (*device)->control_channel, channel );
    }
    
    return 0;
}

/** LOCAL FUNCTIONS ******************************************************/
