8 Link Library API Link Component API
API Reference Manual  /  8 Link Library API  / 

Link Library API

SIMLINK_config_remove_value()

NAME
SIMLINK_config_remove_value — remove a link configuration parameter
SYNOPSIS
void
SIMLINK_config_remove_value(conf_object_t *link, const char *key);

DESCRIPTION
Make sure that all link objects representing link in the simulation receive a configuration message to remove the configuration parameter key.

Note that this function may delay the transmission if it is not possible to send the configuration message yet. The message will be buffered and send when possible. The ordering of configuration messages is kept when buffering them.

All link objects representing link in the simulation will be called via the remove_config_value() function declared in link_type_t, including the one initiating the message.

RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link, where the ID of the endpoints present on the link is kept as a configuration parameter:
static void
ser_link_ep_pre_delete_instance(conf_object_t *ep)
{
        char ep_id[19];
        snprintf(ep_id, sizeof(ep_id), "ep%llx", SIMLINK_endpoint_id(ep));
        SIMLINK_config_remove_value(SIMLINK_endpoint_link(ep), ep_id);
        SIMLINK_endpoint_disconnect(ep);
}

SEE ALSO
SIMLINK_config_update_value, link_type_t

SIMLINK_config_update_value()

NAME
SIMLINK_config_update_value — update a link configuration parameter
SYNOPSIS
void
SIMLINK_config_update_value(conf_object_t *link, const char *key,
                            const frags_t *value);

DESCRIPTION
Make sure that all link objects representing link in the simulation will receive a configuration message for the new value of the configuration parameter key. Both key and value are completely link-specific and transported as-is to all objects.

Note that this function may delay the transmission if it is not possible to send the configuration message yet. The message will be buffered and send when possible. The ordering of configuration messages is kept when buffering them.

All link objects representing link in the simulation will be called via the update_config_value() function declared in link_type_t, including the one initiating the message.

RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ethernet_switch, where the ID of all snooper endpoints present on the link are sent as a configuration parameter so they are included even when a packet can be sent directly to the receiver.
static void
snoop_ep_finalize_instance(conf_object_t *ep)
{
        ep_finalize_instance(ep);

        /* Tell all endpoints that there's a new snoop in town. */
        char ep_id[17];
        snprintf(ep_id, sizeof(ep_id), "%llx", SIMLINK_endpoint_id(ep));
        frags_t value;
        frags_init(&value); /* empty value, just to put the
                               key in the database */
        SIMLINK_config_update_value(
                SIMLINK_endpoint_link(ep), ep_id, &value);
}

SEE ALSO
SIMLINK_config_remove_value, link_type_t

SIMLINK_endpoint_clock()

NAME
SIMLINK_endpoint_clock — return endpoint's clock
SYNOPSIS
conf_object_t *
SIMLINK_endpoint_clock(const conf_object_t *ep_obj);

DESCRIPTION
Return the endpoint ep's associated clock object. It will be either the clock object corresponding to the device connected to the endpoint, or the clock chosen when using the endpoint for snooping.
RETURN VALUE
The associated clock object. This function might return NULL if the device associated to an endpoint does not have its queue attribute set. This indicates a configuration problem, as the device would be unable to send or receive link messages.
EXECUTION CONTEXT
Cell Context
SEE ALSO
SIMLINK_endpoint_device

SIMLINK_endpoint_dev_name()

NAME
SIMLINK_endpoint_dev_name — return the name of the device or snooper to which an endpoint is connected
SYNOPSIS
const char *
SIMLINK_endpoint_dev_name(const conf_object_t *ep_obj, buffer_t scratch);

DESCRIPTION
Return the name of the device or snooper to which the endpoint ep is connected. This function takes an additional scratch parameter that is meant to provide space for putting together the answer when necessary, without allocating any memory. If scratch is used by SIMLINK_endpoint_dev_name() but is not long enough, the name will be truncated.

This function is provided for logging purposes.

RETURN VALUE
The name of the device or snooper the endpoint is connected to
EXECUTION CONTEXT
Cell Context
EXAMPLE
Example from ethernet_hub, to print the name of the device or snooper to which a frame is delivered:
#define BUFFER_T(buf) (buffer_t){ .len = sizeof(buf), .data = buf }

static void
deliver_hub(conf_object_t *ep, const link_message_t *msgdata)
{
        uint8 buf[1000];
        SIM_LOG_INFO(3, ep, 0, "delivering to %s",
                     SIMLINK_endpoint_dev_name(ep, BUFFER_T(buf)));

SEE ALSO
SIMLINK_endpoint_is_device, SIMLINK_endpoint_device

SIMLINK_endpoint_device()

NAME
SIMLINK_endpoint_device — return the device to which an endpoint is connected
SYNOPSIS
conf_object_t *
SIMLINK_endpoint_device(const conf_object_t *ep_obj);

DESCRIPTION
Return the device to which the endpoint ep is connected. If the endpoint is not connected to a device, this function will trigger an assertion failure. This can be checked with SIMLINK_endpoint_is_device().
RETURN VALUE
The device to which the endpoint is connected
EXECUTION CONTEXT
Cell Context
EXAMPLE
Example from datagram_link:
static void
deliver(conf_object_t *ep, const link_message_t *lm)
{
        const datagram_link_message_t *m = 
                (const datagram_link_message_t *)lm;
        conf_object_t *dev = SIMLINK_endpoint_device(ep);
        const char *port = SIMLINK_endpoint_port(ep);
        const datagram_link_interface_t *dli =
                SIM_c_get_port_interface(dev, "datagram_link", port);
        if (dli)
                dli->receive(dev, m->payload);
        else
                SIM_LOG_ERROR(ep, 0, "Device does not implement"
                              " datagram_link interface");
}

SEE ALSO
SIMLINK_endpoint_is_device, SIMLINK_endpoint_dev_name

SIMLINK_endpoint_disconnect()

NAME
SIMLINK_endpoint_disconnect — disconnect an endpoint object
SYNOPSIS
void
SIMLINK_endpoint_disconnect(conf_object_t *ep_obj);

DESCRIPTION
Disconnect the endpoint object ep_obj from its link. This function is intended to be called in the pre_delete_instance() method of an endpoint class. It should never be called in other circumstances, as endpoint objects should not be reused. Note that once the endpoint has been disconnected, it cannot be used for calls to Link Library API functions that takes an endpoint as argument.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link:
static void
ser_link_ep_pre_delete_instance(conf_object_t *ep)
{
        char ep_id[19];
        snprintf(ep_id, sizeof(ep_id), "ep%llx", SIMLINK_endpoint_id(ep));
        SIMLINK_config_remove_value(SIMLINK_endpoint_link(ep), ep_id);
        SIMLINK_endpoint_disconnect(ep);
}

SIMLINK_endpoint_finalize()

NAME
SIMLINK_endpoint_finalize — finalize an endpoint object
SYNOPSIS
void
SIMLINK_endpoint_finalize(conf_object_t *ep_obj);

DESCRIPTION
Finalize the endpoint object ep_obj. This function is intended to be called in the finalize_instance() method of an endpoint class.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link:
static void
ser_link_ep_finalize_instance(conf_object_t *ep)
{
        SIMLINK_endpoint_finalize(ep);
}

SEE ALSO
SIMLINK_endpoint_init

SIMLINK_endpoint_id()

NAME
SIMLINK_endpoint_id — return endpoint's ID
SYNOPSIS
uint64
SIMLINK_endpoint_id(const conf_object_t *ep);

DESCRIPTION
Return the endpoint ep's ID.
RETURN VALUE
The endpoint ID
EXECUTION CONTEXT
Cell Context
EXAMPLE
In ser_link, the endpoints IDs are kept as configuration values so they are known in all link objects:
static void
ser_link_ep_pre_delete_instance(conf_object_t *ep)
{
        char ep_id[19];
        snprintf(ep_id, sizeof(ep_id), "ep%llx", SIMLINK_endpoint_id(ep));
        SIMLINK_config_remove_value(SIMLINK_endpoint_link(ep), ep_id);
        SIMLINK_endpoint_disconnect(ep);
}

SEE ALSO
SIMLINK_find_endpoint_by_id

SIMLINK_endpoint_init()

NAME
SIMLINK_endpoint_init — initialize an endpoint object
SYNOPSIS
void
SIMLINK_endpoint_init(conf_object_t *obj, bool snoop);

DESCRIPTION
Initialize the endpoint object obj. Whether the endpoint is connected to a device or a snooper function is determined by the snoop parameter. This function is intended to be called in the init_object() method of an endpoint class.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from datagram_link:
static void *
datagram_link_endpoint_init_object(conf_object_t *obj, void *data)
{
        datagram_link_endpoint_t *dlep =
                (datagram_link_endpoint_t *)obj;
        SIMLINK_endpoint_init(&dlep->obj, false);
        return dlep;
}

SEE ALSO
SIMLINK_register_endpoint_class, SIMLINK_register_snoop_endpoint_class, SIMLINK_endpoint_finalize

SIMLINK_endpoint_is_device()

NAME
SIMLINK_endpoint_is_device — return whether an endpoint is connected to a device
SYNOPSIS
bool
SIMLINK_endpoint_is_device(const conf_object_t *ep);

DESCRIPTION
Return whether the endpoint ep is connected to a device (as opposed to a link snooper).
RETURN VALUE
true if the endpoint is connected to a device, false otherwise
EXECUTION CONTEXT
Cell Context
EXAMPLE
Example from ethernet_switch, where frames are delivered either to a device endpoint or to a snoop endpoint, using different methods:
static void
switch_deliver_frame(conf_object_t *link, conf_object_t *ep,
                     vlan_tag_t vlan_tag, uint64 src_epid,
                     const frags_t *frame)
{
        eth_frame_crc_status_t crc_status = Eth_Frame_CRC_Match;

        if (SIMLINK_endpoint_is_device(ep)) {
                switch_ep_t *swep = (switch_ep_t *)ep;
                if (frags_len(frame) > 12) {
                        uint8 src_mac[6];
                        frags_extract_slice(frame, src_mac, 6, 6);
                        learn(link, swep, vlan_tag, src_mac, src_epid);
                }
                swep->cep.ifc->frame(SIMLINK_endpoint_device(ep), frame,
                                     crc_status);
        } else {
                snoop_ep_t *snoop = (snoop_ep_t *)ep;
                deliver_to_snoop(snoop->snoop_fun, snoop->user_data,
                                 SIMLINK_endpoint_clock(ep), frame,
                                 crc_status);
        }
}

SEE ALSO
SIMLINK_endpoint_device, SIMLINK_endpoint_port

SIMLINK_endpoint_link()

NAME
SIMLINK_endpoint_link — return endpoint's link
SYNOPSIS
conf_object_t *
SIMLINK_endpoint_link(const conf_object_t *ep);

DESCRIPTION
Return the link object to which the endpoint ep is connected.
RETURN VALUE
The link object
EXECUTION CONTEXT
Cell Context
EXAMPLE
In ser_link, an endpoint needs to check the maximum size of its buffer by querying its link object:
static void
deliver(conf_object_t *ep, const link_message_t *msgd)
{
        ser_link_endpoint_t *slep = (ser_link_endpoint_t *)ep;
        ser_link_impl_t *slink = (ser_link_impl_t *)SIMLINK_endpoint_link(ep);
        ser_link_message_t *msg = (ser_link_message_t *)msgd;
        switch (msg->msgtype) {
        case MSG_Char:

SIMLINK_endpoint_port()

NAME
SIMLINK_endpoint_port — return the device's port to which an endpoint is connected
SYNOPSIS
const char *
SIMLINK_endpoint_port(const conf_object_t *ep_obj);

DESCRIPTION
Return the device's port to which the endpoint ep is connected. If the endpoint is not connected to a device, this function will trigger an assertion failure. This can be checked with SIMLINK_endpoint_is_device().

The port returned might be NULL, which means that the device is implementing a classic interface rather than a port interface.

RETURN VALUE
The device's port to which the endpoint is connected, or NULL if no port is used
EXECUTION CONTEXT
Cell Context
EXAMPLE
Example from ser_link, where a pointer to the interface to call for delivery is kept in the endpoint structure:
static void
ser_link_ep_finalize_instance(conf_object_t *ep)
{
        SIMLINK_endpoint_finalize(ep);
}

SEE ALSO
SIMLINK_endpoint_is_device, SIMLINK_endpoint_device

SIMLINK_finalize()

NAME
SIMLINK_finalize — finalize a link object
SYNOPSIS
void
SIMLINK_finalize(conf_object_t *obj);

DESCRIPTION
Finalize the link object obj. This function is intended to be called in the finalize_instance() method of a link class.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link:
static void
datagram_link_finalize_instance(conf_object_t *obj)
{
        SIMLINK_finalize(obj);
}

SEE ALSO
SIMLINK_init

SIMLINK_find_endpoint_by_id()

NAME
SIMLINK_find_endpoint_by_id — return an endpoint object given its ID
SYNOPSIS
conf_object_t *
SIMLINK_find_endpoint_by_id(conf_object_t *link, uint64 id);

DESCRIPTION
Return the endpoint object with the ID id if the endpoint is connected to the link link, or NULL otherwise.
RETURN VALUE
Endpoint object, or NULL if not found
EXECUTION CONTEXT
Cell Context
SEE ALSO
SIMLINK_endpoint_id

SIMLINK_init()

NAME
SIMLINK_init — initialize a link object
SYNOPSIS
void
SIMLINK_init(conf_object_t *obj, const link_type_t *type);

DESCRIPTION
Initialize the link object obj. The link specific functions that will be called from the link library are gathered in the link_type_t type argument. This function is intended to be called in the init_object() method of a link.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link:
static const link_type_t ser_link_type = {
        .msg_to_attr = msg_to_attr,
        .msg_from_attr = msg_from_attr,
        .free_msg = free_message,
        .marshal = marshal,
        .unmarshal = unmarshal,
        .deliver = deliver,
        .update_config_value = link_config_value_updated,
        .remove_config_value = link_config_value_removed,
        .device_changed = ser_link_ep_device_changed
};

static conf_object_t *
ser_link_alloc_object(void *arg)
{
        ser_link_impl_t *slink = MM_ZALLOC(1, ser_link_impl_t);
        return &slink->obj;
}

static void *
ser_link_init_object(conf_object_t *obj, void *arg)
{
        ser_link_impl_t *slink = (ser_link_impl_t *)obj;
        SIMLINK_init(&slink->obj, &ser_link_type);
        slink->buffer_size = 10; /* a reasonable default value? */
        return obj;
}

SEE ALSO
SIMLINK_register_class, SIMLINK_finalize, link_type_t

SIMLINK_init_library()

NAME
SIMLINK_init_library — initialize the link library
SYNOPSIS
void
SIMLINK_init_library(void);

DESCRIPTION
Initialize the link library. This function is meant to be called in the init_local() function of a module linked to the library.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from datagram_link:
void
init_local(void)
{
        /* The link library must always be initialised first. */
        SIMLINK_init_library();

SIMLINK_init_message()

NAME
SIMLINK_init_message — initialize a link message
SYNOPSIS
void
SIMLINK_init_message(link_message_t *msg);

DESCRIPTION
Initialize the generic part of a link message.
RETURN VALUE
None
EXECUTION CONTEXT
Cell Context
EXAMPLE
Example from datagram_link:
static link_message_t *
new_datagram_message(const uint8 *data, size_t len)
{
        datagram_link_message_t *m = MM_MALLOC(1, datagram_link_message_t);
        SIMLINK_init_message(&m->common);
        uint8 *d = MM_MALLOC(len, uint8);
        memcpy(d, data, len);
        m->payload = (bytes_t){.data = d, .len = len};
        return &m->common;
}
                                                      

SEE ALSO
link_type_t

SIMLINK_pre_delete()

NAME
SIMLINK_pre_delete — clean-up before link deletion
SYNOPSIS
void
SIMLINK_pre_delete(conf_object_t *obj);

DESCRIPTION
Performs clean-up operations before a link object can be safely deleted. This function is intended to be called in the pre_delete_instance() method of a link class.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link:
static void
ser_link_pre_delete_instance(conf_object_t *obj)
{
        SIMLINK_pre_delete(obj);
}

SIMLINK_register_class()

NAME
SIMLINK_register_class — register a link class
SYNOPSIS
void
SIMLINK_register_class(conf_class_t *cls);

DESCRIPTION
Complete the class cls with the necessary attributes and interfaces to be a usable link class. This function is meant to be called after cls has been obtained from SIM_register_class.
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link:
void
init_local(void)
{
        SIMLINK_init_library();

        const class_data_t link_cls_funcs = {
                .alloc_object = ser_link_alloc_object,
                .init_object = ser_link_init_object,
                .finalize_instance = ser_link_finalize_instance,
                .pre_delete_instance = ser_link_pre_delete_instance,
                .delete_instance = ser_link_delete_instance,
                .class_desc = "model of serial link",
                .description = "Serial link"
        };
        conf_class_t *link_cls = SIM_register_class("ser-link-impl",
                                                    &link_cls_funcs);
        SIMLINK_register_class(link_cls);
        SIM_register_typed_attribute(
                link_cls, "buffer_size", get_link_buffer_size, NULL,
                set_link_buffer_size, NULL, Sim_Attr_Optional, "i", NULL,
                "The number of characters that the link may buffer. Must"
                " be at least one.");

        const class_data_t ep_cls_funcs = {
                .alloc_object = ser_link_ep_alloc_object,
                .init_object = ser_link_ep_init_object,
                .finalize_instance = ser_link_ep_finalize_instance,
                .pre_delete_instance = ser_link_ep_pre_delete_instance,
                .delete_instance = ser_link_ep_delete_instance,
                .class_desc =  "serial link endpoint",
                .description = "Serial link endpoint"
        };
        conf_class_t *ep_cls = SIM_register_class("ser-link-endpoint",
                                                  &ep_cls_funcs);
        SIMLINK_register_endpoint_class(ep_cls, "[s]|[si]");
        

SEE ALSO
SIMLINK_init, SIMLINK_finalize

SIMLINK_register_endpoint_class()

NAME
SIMLINK_register_endpoint_class — register a link endpoint class
SYNOPSIS
void
SIMLINK_register_endpoint_class(conf_class_t *cls, const char *msg_type);

DESCRIPTION
Complete the class cls with the necessary attributes and interfaces to be a usable link endpoint class. This function is meant to be called after cls has been obtained from SIM_register_class.

msg_type is a string defining the type of the attribute representing a link message, as returned by msg_to_attr() in link_type_t.

RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from ser_link:
void
init_local(void)
{
        SIMLINK_init_library();

        const class_data_t link_cls_funcs = {
                .alloc_object = ser_link_alloc_object,
                .init_object = ser_link_init_object,
                .finalize_instance = ser_link_finalize_instance,
                .pre_delete_instance = ser_link_pre_delete_instance,
                .delete_instance = ser_link_delete_instance,
                .class_desc = "model of serial link",
                .description = "Serial link"
        };
        conf_class_t *link_cls = SIM_register_class("ser-link-impl",
                                                    &link_cls_funcs);
        SIMLINK_register_class(link_cls);
        SIM_register_typed_attribute(
                link_cls, "buffer_size", get_link_buffer_size, NULL,
                set_link_buffer_size, NULL, Sim_Attr_Optional, "i", NULL,
                "The number of characters that the link may buffer. Must"
                " be at least one.");

        const class_data_t ep_cls_funcs = {
                .alloc_object = ser_link_ep_alloc_object,
                .init_object = ser_link_ep_init_object,
                .finalize_instance = ser_link_ep_finalize_instance,
                .pre_delete_instance = ser_link_ep_pre_delete_instance,
                .delete_instance = ser_link_ep_delete_instance,
                .class_desc =  "serial link endpoint",
                .description = "Serial link endpoint"
        };
        conf_class_t *ep_cls = SIM_register_class("ser-link-endpoint",
                                                  &ep_cls_funcs);
        SIMLINK_register_endpoint_class(ep_cls, "[s]|[si]");
        

SEE ALSO
SIMLINK_endpoint_finalize

SIMLINK_register_snoop_endpoint_class()

NAME
SIMLINK_register_snoop_endpoint_class — register a link snoop endpoint class
SYNOPSIS
void
SIMLINK_register_snoop_endpoint_class(conf_class_t *cls);

DESCRIPTION
Complete the class cls with the necessary attributes and interfaces to be a usable link snoop endpoint class. This function is meant to be called after cls has been obtained from SIM_register_class().
RETURN VALUE
None
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from the new Ethernet links:
void
init_local(void)
{
        SIMLINK_init_library();
        init_eth_hub_link();
        init_eth_cable_link();
        init_eth_switch_link();
	init_ethernet_crc_table();

        const class_data_t snoop_ep_cls_funcs = {
                .alloc_object = snoop_ep_alloc_object,
                .init_object = snoop_ep_init_object,
                .finalize_instance = ep_finalize_instance,
                .pre_delete_instance = snoop_ep_pre_delete_instance,
                .delete_instance = snoop_ep_delete_instance,
                .description = "Ethernet link snoop endpoint",
                .class_desc = "an Ethernet link snoop endpoint",
                .kind = Sim_Class_Kind_Pseudo,
        };
        snoop_ep_cls = SIM_register_class("eth-link-snoop-endpoint",
                                          &snoop_ep_cls_funcs);
        SIMLINK_register_snoop_endpoint_class(snoop_ep_cls);
}

SEE ALSO
SIMLINK_endpoint_finalize

SIMLINK_send_message()

NAME
SIMLINK_send_message — send a link message
SYNOPSIS
void
SIMLINK_send_message(conf_object_t *src_ep_obj,
                  uint64 dst_id, link_message_t *msg);

DESCRIPTION
Send a message msg from the endpoint src_ep_obj to the destination ID dst_id. The destination may be any valid endpoint ID on the link or LINK_BROADCAST_ID, which will send the message to all endpoints on the link except the sender.

It is important to note that the ownership of the message msg is passed to the link library when calling SIMLINK_send_message(). When returning, msg may have been already deallocated and should not be used anymore.

RETURN VALUE
None
EXECUTION CONTEXT
Cell Context
EXAMPLE
Example from datagram_link:
static void
receive(conf_object_t *NOTNULL ep, bytes_t msg)
{
        SIMLINK_send_message(ep, LINK_BROADCAST_ID, 
                             new_datagram_message(msg.data, msg.len));
}

SEE ALSO
SIMLINK_send_message_multi, link_message_t, link_type_t

SIMLINK_send_message_multi()

NAME
SIMLINK_send_message_multi — send a link message to multiple recipients
SYNOPSIS
void
SIMLINK_send_message_multi(conf_object_t *src_ep_obj, unsigned num_dsts,
                           const uint64 *dst_ids, link_message_t *msg);

DESCRIPTION
Send a message msg from the endpoint src_ep_obj to the destinations IDs dst_ids. The length of the dst_ids list is provided by num_dsts. Each destination should be a valid endpoint ID on the link. It is not allowed to be LINK_BROADCAST_ID.

It is important to note that the ownership of the message msg is passed to the link library when calling SIMLINK_send_message_multi(). When returning, msg may have been already deallocated and should not be used anymore.

RETURN VALUE
None
EXECUTION CONTEXT
Cell Context
EXAMPLE
Example from signal_link, which keeps a list of endpoints to send to and specifically directs its messages to the appropriate endpoints:
static void
send_message(signal_link_endpoint_t *slep, link_message_t *msg)
{
        signal_link_t *slink = 
                (signal_link_t *)SIMLINK_endpoint_link(&slep->obj);
        int num_dsts = ht_num_entries_int(&slink->receivers);
        uint64 dst_ids[num_dsts];
        memset(dst_ids, 0, num_dsts * sizeof(uint64));
        int i = 0;
        HT_FOREACH_INT(&slink->receivers, it)
                dst_ids[i++] = ht_iter_int_key(it);
        SIMLINK_send_message_multi(&slep->obj, num_dsts, dst_ids, msg);
}

SEE ALSO
SIMLINK_send_message, link_message_t, link_type_t

SIMLINK_snoop_endpoint_create()

NAME
SIMLINK_snoop_endpoint_create — create a snoop endpoint object
SYNOPSIS
conf_object_t *
SIMLINK_snoop_endpoint_create(conf_class_t *cls, conf_object_t *link,
                              conf_object_t *clock,
                              attr_value_t attrs);

DESCRIPTION
This method returns an already created snoop endpoint object. It is meant to be used when implementing a snoop_attach interface, where endpoints can not be created using components as it is usually done. SIMLINK_snoop_endpoint_create() takes as arguments the class of the snoop endpoint object cls, the link object link, and a list of attributes to set, in the same form as provided to SIM_create_object().
RETURN VALUE
A snoop endpoint object
EXECUTION CONTEXT
Global Context
EXAMPLE
Example from the new Ethernet links:
static conf_object_t *
default_attach_snoop(conf_object_t *obj, conf_object_t *clock,
                     ethernet_link_snoop_t snoop_fun, lang_void *user_data)
{
        common_link_t *clink = (common_link_t *)obj;
        attach_snoop_helper(clink, clock);
        attr_value_t attrs = SIM_make_attr_list(0);
        snoop_ep_t *snoop = (snoop_ep_t *)SIMLINK_snoop_endpoint_create(
                snoop_ep_cls, &clink->obj, clock, attrs);
        SIM_attr_free(&attrs);
        snoop->snoop_fun = snoop_fun;
        snoop->user_data = user_data;
        return &snoop->cep.obj;
}

link_message_t

NAME
link_message_t
SYNOPSIS
typedef struct link_message link_message_t;
DESCRIPTION
Generic part of a link message. This structure should always be the first member of the link message data structure, so that the link library can access the generic part with a simple cast.
EXAMPLE
The datagram_link example defines its link message in the following way:
typedef struct {
        link_message_t common;               /* should always be first */
        /* The actual data in the message - in our case an allocated
           byte string owned by this structure. */
        bytes_t payload;
} datagram_link_message_t;

SEE ALSO
link_type_t, SIMLINK_init_message

link_type_t

NAME
link_type_t
SYNOPSIS
typedef struct {
        attr_value_t (*msg_to_attr)(conf_object_t *link, 
                                    const link_message_t *msg);
        link_message_t *(*msg_from_attr)(conf_object_t *link, 
                                         attr_value_t attr);
        void (*free_msg)(conf_object_t *link, link_message_t *msg);

        void (*marshal)(conf_object_t *link, const link_message_t *msg,
                        void (*finish)(void *data, const frags_t *msg),
                        void *finish_data);
        link_message_t *(*unmarshal)(conf_object_t *link, 
                                     const frags_t *msg);

        void (*deliver)(conf_object_t *ep, const link_message_t *msg);

        void (*update_config_value)(conf_object_t *link, const char *key,
                                    const frags_t *value);
        void (*remove_config_value)(conf_object_t *link, const char *key);
        void (*device_changed)(conf_object_t *ep, conf_object_t *old_dev);
} link_type_t;

DESCRIPTION
Functions to be defined by the specific link implementation.

These functions can be classified in four groups:

The first five functions are related to the link-specific messages.

All five functions can be called in any execution context and should be thread-safe. They all take the link object as argument, in case it contains information necessary to perform the operation. As the link object is shared between the cells in which it is connected, it should not be modified during execution. Mutable state should be kept in the endpoint objects instead.

msg_to_attr() transforms the message msg into an attr_value_t value. It is used to checkpoint in-flight messages waiting to be delivered. The value returned will be passed unchanged as argument attr to msg_from_attr() when loading a checkpoint with pending link messages. Neither function is expected to return an error, although msg_from_attr() is allowed to return NULL when translating a message it does not care to restore. This can be useful to keep checkpoint compatibility with older versions of the same link that do not always have the same message protocol.

Using the datagram_link as an example, the datagram-link message is defined as:

typedef struct {
        link_message_t common;               /* should always be first */
        /* The actual data in the message - in our case an allocated
           byte string owned by this structure. */
        bytes_t payload;
} datagram_link_message_t;

msg_to_attr() and msg_from_attr() are thus defined as:

static attr_value_t
msg_to_attr(conf_object_t *link, const link_message_t *lm)
{
        const datagram_link_message_t *m = 
                (const datagram_link_message_t *)lm;
        return SIM_make_attr_data(m->payload.len, m->payload.data);
}

static link_message_t *
msg_from_attr(conf_object_t *link, attr_value_t attr)
{
        return new_datagram_message(SIM_attr_data(attr),
                                    SIM_attr_data_size(attr));
}

free_msg() is called when the message msg has been delivered to all its destinations and is no longer needed. All memory allocated for msg is expected to be freed, including msg itself. The datagram_link defines free_msg() as:

static void
free_msg(conf_object_t *link, link_message_t *lm)
{
        datagram_link_message_t *m = (datagram_link_message_t *)lm;
        MM_FREE((uint8 *)m->payload.data);
        m->payload.data = NULL;
        MM_FREE(m);
}

marshal() is called when the message msg should be transmitted over a distributed simulation. Its purpose is to serialize the message into a frags_t representation. Rather than returning the marshaled message, marshal() takes the finish and finish_data arguments, that it is expected to call once the message has been marshaled.

The reason behind this mechanism is that it allows marshal() to perform its operations with a frags_t variable allocated on the stack, and thus to skip any heap allocation when sending the message. In case memory was allocated anyway, it should be freed just after finish has returned.

static void
marshal(conf_object_t *link, const link_message_t *lm,
        void (*finish)(void *data, const frags_t *msg), 
        void *finish_data)
{
        const datagram_link_message_t *m = 
                (const datagram_link_message_t *)lm;

        /* Our message just consists of a byte string, 
           so this is very easy. */
        frags_t buf;
        frags_init_add(&buf, m->payload.data, m->payload.len);
        finish(finish_data, &buf);
}

unmarshal() does the opposite of marshal(): it takes a serialized frags_t representation of the message called data and returns a newly allocated link message.

static link_message_t *
unmarshal(conf_object_t *link, const frags_t *data)
{
        size_t len = frags_len(data);
        uint8 bytes[len];
        frags_extract(data, bytes);
        return new_datagram_message(bytes, len);
}

Endpoint Configuration
Link endpoints are created as needed by the link component. Depending on how they are created, they may not know yet which device they are connected to, so it might not be possible yet, for example, to cache the device's communication interface in the endpoint's finalize() function. Additionally, there are cases where the device the endpoint talks to may be changed, such as when inserting a probe object to listen to the traffic.

In all of these cases, the device_changed() callback will be called when the endpoint's device attribute is changed and the endpoint has reached to finalize phase. In that callback, the new device can be obtained via SIMLINK_endpoint_device() and additional operations, such as interface caching, can be safely performed. The old device the endpoint was connected to is provided for convenience as an argument to device_changed().

Note that if no device related operations are necessary, this callback may be left unimplemented.

The ser_link implementation of device_changed is the following:

static void
ser_link_ep_device_changed(conf_object_t *ep, conf_object_t *old_dev)
{
        ser_link_endpoint_t *slep = (ser_link_endpoint_t *)ep;
        slep->serial_ifc = SIM_c_get_port_interface(
                SIMLINK_endpoint_device(ep), SERIAL_DEVICE_INTERFACE,
                SIMLINK_endpoint_port(ep));
 
        if (!old_dev) {
                char ep_id[19];
                snprintf(ep_id, sizeof(ep_id), "ep%llx", 
                         SIMLINK_endpoint_id(ep));
                frags_t value;
                frags_init(&value);
                SIMLINK_config_update_value(SIMLINK_endpoint_link(ep), 
                                            ep_id, &value);
        }
}

Message Delivery
Messages are delivered to the link by calling the deliver() function. The arguments of deliver() are the endpoint ep that received the message and the message msg itself. The implementation of deliver() is expected to call the correct device's function to deliver the message.

Note that deliver() can be called in any execution context and should be thread-safe. The link object is shared between the cells in which it is connected, and should not be modified during execution. Mutable state should be kept in the endpoint objects instead.

The datagram_link implementation of deliver() is the following:

static void
deliver(conf_object_t *ep, const link_message_t *lm)
{
        const datagram_link_message_t *m = 
                (const datagram_link_message_t *)lm;
        conf_object_t *dev = SIMLINK_endpoint_device(ep);
        const char *port = SIMLINK_endpoint_port(ep);
        const datagram_link_interface_t *dli =
                SIM_c_get_port_interface(dev, "datagram_link", port);
        if (dli)
                dli->receive(dev, m->payload);
        else
                SIM_LOG_ERROR(ep, 0, "Device does not implement"
                              " datagram_link interface");
}

Configuration
The last two functions of link_type_t are taking care of the link configuration itself. In the same way messages needs to be marshaled when sent over a network, the global link configuration needs to be agreed upon when running the simulation in several processes.

update_config_value() is called whenever a configuration parameter has been added or updated. The configuration parameter's name is provided as key and its new value as value, encoded as a frags_t.

remove_config_value() is called whenever the configuration value key has been removed.

The interpretation of the link configuration messages is link specific. The only configuration parameter that is defined by the link library itself is goal_latency. This is handled entirely internally, although with the same mechanism as exposed here. Configuration changes are initiated by the link objects themselves with the Link Library API functions SIMLINK_config_update_value() and SIMLINK_config_remove_value().

Note that the link object that initiates the configuration change is also called back via update_config_value() and remove_config_value(). Note also that the configuration changes may be buffered and sent later if they are initiated too soon for the configuration message to propagate.

Configuration changes should only be initiated while in Global Context, so the two configuration functions above will only be called in Global Context. This allows them to modify properties of the link object itself without needing to care about thread safety.

As an example, here is how ser_link defines these two functions. The serial link keeps track of all endpoints connected to it by saving their ID as a configuration parameter. It also uses a configurable buffer size.

Finally, it is important to note that these two callbacks may be called from a non-execution thread. They should call the Simics API only via SIM_thread_safe_callback(). This includes calling the SIM_log_* functions.

static void
link_config_value_updated(conf_object_t *link, const char *key, 
                          const frags_t *msg)
{
        ser_link_impl_t *slink = (ser_link_impl_t *)link;
        if (strncmp(key, "ep", 2) == 0) {
                uint64 ep_id = strtoull(key + 2, NULL, 16);
                SIM_LOG_INFO(4, &slink->obj, 0,
                             "Add endpoint: 0x%llx", ep_id);
                ht_update_int(&slink->endpoints, ep_id, NULL);
        } else if (strcmp(key, "buffer_size") == 0) {
                slink->buffer_size = frags_extract_be32(msg, 0);
        } else {
                ASSERT(false);
        }
}

static void
link_config_value_removed(conf_object_t *link, const char *key)
{
        ser_link_impl_t *slink = (ser_link_impl_t *)link;
        if (strncmp(key, "ep", 2) == 0) {
                uint64 ep_id = strtoull(key + 2, NULL, 16);
                SIM_LOG_INFO(4, &slink->obj, 0,
                             "Remove endpoint: 0x%llx", ep_id);
                ht_remove_int(&slink->endpoints, ep_id);
        } else {
                ASSERT(false);
        }
}

SEE ALSO
link_message_t, SIMLINK_init

8 Link Library API Link Component API