HelenOS sources

root/uspace/lib/nic/src/nic_impl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nic_get_state_impl
  2. nic_set_state_impl
  3. nic_send_frame_impl
  4. nic_callback_create_impl
  5. nic_get_address_impl
  6. nic_get_stats_impl
  7. nic_unicast_get_mode_impl
  8. nic_unicast_set_mode_impl
  9. nic_multicast_get_mode_impl
  10. nic_multicast_set_mode_impl
  11. nic_broadcast_get_mode_impl
  12. nic_broadcast_set_mode_impl
  13. nic_blocked_sources_get_impl
  14. nic_blocked_sources_set_impl
  15. nic_vlan_get_mask_impl
  16. nic_vlan_set_mask_impl
  17. nic_wol_virtue_add_impl
  18. nic_wol_virtue_remove_impl
  19. nic_wol_virtue_probe_impl
  20. nic_wol_virtue_list_impl
  21. nic_wol_virtue_get_caps_impl
  22. nic_poll_get_mode_impl
  23. nic_poll_set_mode_impl
  24. nic_poll_now_impl
  25. nic_default_handler_impl
  26. nic_open_impl
  27. nic_close_impl

/*
 * Copyright (c) 2011 Radim Vansa
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @addtogroup libnic
 * @{
 */
/**
 * @file
 * @brief Default DDF NIC interface methods implementations
 */

#include <errno.h>
#include <str_error.h>
#include <ipc/services.h>
#include <ns.h>
#include "nic_driver.h"
#include "nic_ev.h"
#include "nic_impl.h"

/**
 * Default implementation of the set_state method. Trivial.
 *
 * @param               fun
 * @param[out]  state
 *
 * @return EOK always.
 */
errno_t nic_get_state_impl(ddf_fun_t *fun, nic_device_state_t *state)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->main_lock);
        *state = nic_data->state;
        fibril_rwlock_read_unlock(&nic_data->main_lock);
        return EOK;
}

/**
 * Default implementation of the set_state method. Changes the internal
 * driver's state, calls the appropriate callback and notifies the NIL service
 * about this change.
 *
 * @param       fun
 * @param       state   The new device's state
 *
 * @return EOK          If the state was changed
 * @return EINVAL       If the state cannot be changed
 */
errno_t nic_set_state_impl(ddf_fun_t *fun, nic_device_state_t state)
{
        if (state >= NIC_STATE_MAX) {
                return EINVAL;
        }

        nic_t *nic_data = nic_get_from_ddf_fun(fun);

        fibril_rwlock_write_lock(&nic_data->main_lock);
        if (nic_data->state == state) {
                /* No change, nothing to do */
                fibril_rwlock_write_unlock(&nic_data->main_lock);
                return EOK;
        }
        if (state == NIC_STATE_ACTIVE) {
                if (nic_data->client_session == NULL) {
                        fibril_rwlock_write_unlock(&nic_data->main_lock);
                        return EINVAL;
                }
        }

        state_change_handler event_handler = NULL;
        switch (state) {
        case NIC_STATE_STOPPED:
                event_handler = nic_data->on_stopping;
                break;
        case NIC_STATE_DOWN:
                event_handler = nic_data->on_going_down;
                break;
        case NIC_STATE_ACTIVE:
                event_handler = nic_data->on_activating;
                break;
        default:
                break;
        }
        if (event_handler != NULL) {
                errno_t rc = event_handler(nic_data);
                if (rc != EOK) {
                        fibril_rwlock_write_unlock(&nic_data->main_lock);
                        return EINVAL;
                }
        }

        if (state == NIC_STATE_STOPPED) {
                /* Notify upper layers that we are reseting the MAC */
                errno_t rc = nic_ev_addr_changed(nic_data->client_session,
                    &nic_data->default_mac);
                nic_data->poll_mode = nic_data->default_poll_mode;
                memcpy(&nic_data->poll_period, &nic_data->default_poll_period,
                    sizeof(struct timespec));
                if (rc != EOK) {
                        /*
                         * We have already ran the on stopped handler, even if we
                         * terminated the state change we would end up in undefined state.
                         * Therefore we just log the problem.
                         */
                }

                fibril_rwlock_write_lock(&nic_data->stats_lock);
                memset(&nic_data->stats, 0, sizeof(nic_device_stats_t));
                fibril_rwlock_write_unlock(&nic_data->stats_lock);

                fibril_rwlock_write_lock(&nic_data->rxc_lock);
                nic_rxc_clear(&nic_data->rx_control);
                /* Reinsert device's default MAC */
                nic_rxc_set_addr(&nic_data->rx_control, NULL,
                    &nic_data->default_mac);
                fibril_rwlock_write_unlock(&nic_data->rxc_lock);
                memcpy(&nic_data->mac, &nic_data->default_mac, sizeof (nic_address_t));

                fibril_rwlock_write_lock(&nic_data->wv_lock);
                nic_wol_virtues_clear(&nic_data->wol_virtues);
                fibril_rwlock_write_unlock(&nic_data->wv_lock);

                /* Ensure stopping period of NIC_POLL_SOFTWARE_PERIODIC */
                nic_sw_period_stop(nic_data);
        }

        nic_data->state = state;

        nic_ev_device_state(nic_data->client_session, state);

        fibril_rwlock_write_unlock(&nic_data->main_lock);

        return EOK;
}

/**
 * Default implementation of the send_frame method.
 * Send messages to the network.
 *
 * @param       fun
 * @param       data    Frame data
 * @param       size    Frame size in bytes
 *
 * @return EOK          If the message was sent
 * @return EBUSY        If the device is not in state when the frame can be sent.
 */
errno_t nic_send_frame_impl(ddf_fun_t *fun, void *data, size_t size)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);

        fibril_rwlock_read_lock(&nic_data->main_lock);
        if (nic_data->state != NIC_STATE_ACTIVE || nic_data->tx_busy) {
                fibril_rwlock_read_unlock(&nic_data->main_lock);
                return EBUSY;
        }

        nic_data->send_frame(nic_data, data, size);
        fibril_rwlock_read_unlock(&nic_data->main_lock);
        return EOK;
}

/**
 * Default implementation of the connect_client method.
 * Creates callback connection to the client.
 *
 * @param       fun
 *
 * @return EOK          On success, or an error code.
 */
errno_t nic_callback_create_impl(ddf_fun_t *fun)
{
        nic_t *nic = nic_get_from_ddf_fun(fun);
        fibril_rwlock_write_lock(&nic->main_lock);

        nic->client_session = async_callback_receive(EXCHANGE_SERIALIZE);
        if (nic->client_session == NULL) {
                fibril_rwlock_write_unlock(&nic->main_lock);
                return ENOMEM;
        }

        fibril_rwlock_write_unlock(&nic->main_lock);
        return EOK;
}

/**
 * Default implementation of the get_address method.
 * Retrieves the NIC's physical address.
 *
 * @param       fun
 * @param       address Pointer to the structure where the address will be stored.
 *
 * @return EOK          If the services were bound
 * @return ELIMIT       If the buffer is too short
 */
errno_t nic_get_address_impl(ddf_fun_t *fun, nic_address_t *address)
{
        assert(address);
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->main_lock);
        memcpy(address, &nic_data->mac, sizeof (nic_address_t));
        fibril_rwlock_read_unlock(&nic_data->main_lock);
        return EOK;
}

/**
 * Default implementation of the get_stats method. Copies the statistics from
 * the drivers data to supplied buffer.
 *
 * @param               fun
 * @param[out]  stats   The buffer for statistics
 *
 * @return EOK (cannot fail)
 */
errno_t nic_get_stats_impl(ddf_fun_t *fun, nic_device_stats_t *stats)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        assert (stats != NULL);
        fibril_rwlock_read_lock(&nic_data->stats_lock);
        memcpy(stats, &nic_data->stats, sizeof (nic_device_stats_t));
        fibril_rwlock_read_unlock(&nic_data->stats_lock);
        return EOK;
}

/**
 * Default implementation of unicast_get_mode method.
 *
 * @param               fun
 * @param[out]  mode            Current operation mode
 * @param[in]   max_count       Max number of addresses that can be written into the
 *                                                      buffer (addr_list).
 * @param[out]  addr_list       Buffer for addresses
 * @param[out]  addr_count      Number of addresses written into the list
 *
 * @return EOK
 */
errno_t nic_unicast_get_mode_impl(ddf_fun_t *fun, nic_unicast_mode_t *mode,
    size_t max_count, nic_address_t *addr_list, size_t *addr_count)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->rxc_lock);
        nic_rxc_unicast_get_mode(&nic_data->rx_control, mode, max_count,
            addr_list, addr_count);
        fibril_rwlock_read_unlock(&nic_data->rxc_lock);
        return EOK;
}

/**
 * Default implementation of unicast_set_mode method.
 *
 * @param               fun
 * @param[in]   mode            New operation mode
 * @param[in]   addr_list       List of unicast addresses
 * @param[in]   addr_count      Number of addresses in the list
 *
 * @return EOK
 * @return EINVAL
 * @return ENOTSUP
 * @return ENOMEM
 */
errno_t nic_unicast_set_mode_impl(ddf_fun_t *fun,
    nic_unicast_mode_t mode, const nic_address_t *addr_list, size_t addr_count)
{
        assert((addr_count == 0 && addr_list == NULL) ||
            (addr_count != 0 && addr_list != NULL));
        size_t i;
        for (i = 0; i < addr_count; ++i) {
                if (addr_list[i].address[0] & 1)
                        return EINVAL;
        }

        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_write_lock(&nic_data->rxc_lock);
        errno_t rc = ENOTSUP;
        if (nic_data->on_unicast_mode_change) {
                rc = nic_data->on_unicast_mode_change(nic_data,
                    mode, addr_list, addr_count);
        }
        if (rc == EOK) {
                rc = nic_rxc_unicast_set_mode(&nic_data->rx_control, mode,
                    addr_list, addr_count);
                /*
                 * After changing the mode the addr db gets cleared, therefore we have
                 * to reinsert also the physical address of NIC.
                 */
                nic_rxc_set_addr(&nic_data->rx_control, NULL, &nic_data->mac);
        }
        fibril_rwlock_write_unlock(&nic_data->rxc_lock);
        return rc;
}

/**
 * Default implementation of multicast_get_mode method.
 *
 * @param               fun
 * @param[out]  mode            Current operation mode
 * @param[in]   max_count       Max number of addresses that can be written into the
 *                                                      buffer (addr_list).
 * @param[out]  addr_list       Buffer for addresses
 * @param[out]  addr_count      Number of addresses written into the list
 *
 * @return EOK
 */
errno_t nic_multicast_get_mode_impl(ddf_fun_t *fun, nic_multicast_mode_t *mode,
    size_t max_count, nic_address_t *addr_list, size_t *addr_count)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->rxc_lock);
        nic_rxc_multicast_get_mode(&nic_data->rx_control, mode, max_count,
            addr_list, addr_count);
        fibril_rwlock_read_unlock(&nic_data->rxc_lock);
        return EOK;
}

/**
 * Default implementation of multicast_set_mode method.
 *
 * @param               fun
 * @param[in]   mode            New operation mode
 * @param[in]   addr_list       List of multicast addresses
 * @param[in]   addr_count      Number of addresses in the list
 *
 * @return EOK
 * @return EINVAL
 * @return ENOTSUP
 * @return ENOMEM
 */
errno_t nic_multicast_set_mode_impl(ddf_fun_t *fun,     nic_multicast_mode_t mode,
    const nic_address_t *addr_list, size_t addr_count)
{
        assert((addr_count == 0 && addr_list == NULL) ||
            (addr_count != 0 && addr_list != NULL));
        size_t i;
        for (i = 0; i < addr_count; ++i) {
                if (!(addr_list[i].address[0] & 1))
                        return EINVAL;
        }

        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_write_lock(&nic_data->rxc_lock);
        errno_t rc = ENOTSUP;
        if (nic_data->on_multicast_mode_change) {
                rc = nic_data->on_multicast_mode_change(nic_data, mode, addr_list, addr_count);
        }
        if (rc == EOK) {
                rc = nic_rxc_multicast_set_mode(&nic_data->rx_control, mode,
                    addr_list, addr_count);
        }
        fibril_rwlock_write_unlock(&nic_data->rxc_lock);
        return rc;
}

/**
 * Default implementation of broadcast_get_mode method.
 *
 * @param               fun
 * @param[out]  mode            Current operation mode
 *
 * @return EOK
 */
errno_t nic_broadcast_get_mode_impl(ddf_fun_t *fun, nic_broadcast_mode_t *mode)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->rxc_lock);
        nic_rxc_broadcast_get_mode(&nic_data->rx_control, mode);
        fibril_rwlock_read_unlock(&nic_data->rxc_lock);
        return EOK;
}

/**
 * Default implementation of broadcast_set_mode method.
 *
 * @param               fun
 * @param[in]   mode            New operation mode
 *
 * @return EOK
 * @return EINVAL
 * @return ENOTSUP
 * @return ENOMEM
 */
errno_t nic_broadcast_set_mode_impl(ddf_fun_t *fun, nic_broadcast_mode_t mode)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_write_lock(&nic_data->rxc_lock);
        errno_t rc = ENOTSUP;
        if (nic_data->on_broadcast_mode_change) {
                rc = nic_data->on_broadcast_mode_change(nic_data, mode);
        }
        if (rc == EOK) {
                rc = nic_rxc_broadcast_set_mode(&nic_data->rx_control, mode);
        }
        fibril_rwlock_write_unlock(&nic_data->rxc_lock);
        return rc;
}

/**
 * Default implementation of blocked_sources_get method.
 *
 * @param               fun
 * @param[in]   max_count       Max number of addresses that can be written into the
 *                                                      buffer (addr_list).
 * @param[out]  addr_list       Buffer for addresses
 * @param[out]  addr_count      Number of addresses written into the list
 *
 * @return EOK
 */
errno_t nic_blocked_sources_get_impl(ddf_fun_t *fun,
    size_t max_count, nic_address_t *addr_list, size_t *addr_count)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->rxc_lock);
        nic_rxc_blocked_sources_get(&nic_data->rx_control,
            max_count, addr_list, addr_count);
        fibril_rwlock_read_unlock(&nic_data->rxc_lock);
        return EOK;
}

/**
 * Default implementation of blocked_sources_set method.
 *
 * @param               fun
 * @param[in]   addr_list       List of blocked addresses
 * @param[in]   addr_count      Number of addresses in the list
 *
 * @return EOK
 * @return EINVAL
 * @return ENOTSUP
 * @return ENOMEM
 */
errno_t nic_blocked_sources_set_impl(ddf_fun_t *fun,
    const nic_address_t *addr_list, size_t addr_count)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_write_lock(&nic_data->rxc_lock);
        if (nic_data->on_blocked_sources_change) {
                nic_data->on_blocked_sources_change(nic_data, addr_list, addr_count);
        }
        errno_t rc = nic_rxc_blocked_sources_set(&nic_data->rx_control,
            addr_list, addr_count);
        fibril_rwlock_write_unlock(&nic_data->rxc_lock);
        return rc;
}

/**
 * Default implementation of vlan_get_mask method.
 *
 * @param               fun
 * @param[out]  mask    Current VLAN mask
 *
 * @return EOK
 * @return ENOENT       If the mask is not set
 */
errno_t nic_vlan_get_mask_impl(ddf_fun_t *fun, nic_vlan_mask_t *mask)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->rxc_lock);
        errno_t rc = nic_rxc_vlan_get_mask(&nic_data->rx_control, mask);
        fibril_rwlock_read_unlock(&nic_data->rxc_lock);
        return rc;
}

/**
 * Default implementation of vlan_set_mask method.
 *
 * @param               fun
 * @param[in]   mask    The new VLAN mask
 *
 * @return EOK
 * @return ENOMEM
 */
errno_t nic_vlan_set_mask_impl(ddf_fun_t *fun, const nic_vlan_mask_t *mask)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_write_lock(&nic_data->rxc_lock);
        errno_t rc = nic_rxc_vlan_set_mask(&nic_data->rx_control, mask);
        if (rc == EOK && nic_data->on_vlan_mask_change) {
                nic_data->on_vlan_mask_change(nic_data, mask);
        }
        fibril_rwlock_write_unlock(&nic_data->rxc_lock);
        return rc;
}

/**
 * Default implementation of the wol_virtue_add method.
 * Create a new WOL virtue.
 *
 * @param[in]   fun
 * @param[in]   type            Type of the virtue
 * @param[in]   data            Data required for this virtue (depends on type)
 * @param[in]   length          Length of the data
 * @param[out]  filter          Identifier of the new virtue
 *
 * @return EOK          If the operation was successfully completed
 * @return EINVAL       If virtue type is not supported or the data are invalid
 * @return ELIMIT       If the driver does not allow to create more virtues
 * @return ENOMEM       If there was not enough memory to complete the operation
 */
errno_t nic_wol_virtue_add_impl(ddf_fun_t *fun, nic_wv_type_t type,
    const void *data, size_t length, nic_wv_id_t *new_id)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        if (nic_data->on_wol_virtue_add == NULL ||
            nic_data->on_wol_virtue_remove == NULL) {
                return ENOTSUP;
        }
        if (type == NIC_WV_NONE || type >= NIC_WV_MAX) {
                return EINVAL;
        }
        if (nic_wol_virtues_verify(type, data, length) != EOK) {
                return EINVAL;
        }
        nic_wol_virtue_t *virtue = malloc(sizeof (nic_wol_virtue_t));
        if (virtue == NULL) {
                return ENOMEM;
        }
        memset(virtue, 0, sizeof(nic_wol_virtue_t));
        if (length != 0) {
                virtue->data = malloc(length);
                if (virtue->data == NULL) {
                        free(virtue);
                        return ENOMEM;
                }
                memcpy((void *) virtue->data, data, length);
        }
        virtue->type = type;
        virtue->length = length;

        fibril_rwlock_write_lock(&nic_data->wv_lock);
        /* Check if we haven't reached the maximum */
        if (nic_data->wol_virtues.caps_max[type] < 0) {
                fibril_rwlock_write_unlock(&nic_data->wv_lock);
                free(virtue->data);
                free(virtue);
                return EINVAL;
        }
        if ((int) nic_data->wol_virtues.lists_sizes[type] >=
            nic_data->wol_virtues.caps_max[type]) {
                fibril_rwlock_write_unlock(&nic_data->wv_lock);
                free(virtue->data);
                free(virtue);
                return ELIMIT;
        }
        /* Call the user-defined add callback */
        errno_t rc = nic_data->on_wol_virtue_add(nic_data, virtue);
        if (rc != EOK) {
                free(virtue->data);
                free(virtue);
                fibril_rwlock_write_unlock(&nic_data->wv_lock);
                return rc;
        }
        rc = nic_wol_virtues_add(&nic_data->wol_virtues, virtue);
        if (rc != EOK) {
                /* If the adding fails, call user-defined remove callback */
                nic_data->on_wol_virtue_remove(nic_data, virtue);
                fibril_rwlock_write_unlock(&nic_data->wv_lock);
                free(virtue->data);
                free(virtue);
                return rc;
        } else {
                *new_id = virtue->id;
                fibril_rwlock_write_unlock(&nic_data->wv_lock);
        }
        return EOK;
}

/**
 * Default implementation of the wol_virtue_remove method.
 * Destroys the WOL virtue.
 *
 * @param[in] fun
 * @param[in] id        WOL virtue identification
 *
 * @return EOK          If the operation was successfully completed
 * @return ENOTSUP      If the function is not supported by the driver or device
 * @return ENOENT       If the virtue identifier is not valid.
 */
errno_t nic_wol_virtue_remove_impl(ddf_fun_t *fun, nic_wv_id_t id)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        if (nic_data->on_wol_virtue_add == NULL ||
            nic_data->on_wol_virtue_remove == NULL) {
                return ENOTSUP;
        }
        fibril_rwlock_write_lock(&nic_data->wv_lock);
        nic_wol_virtue_t *virtue =
            nic_wol_virtues_remove(&nic_data->wol_virtues, id);
        if (virtue == NULL) {
                fibril_rwlock_write_unlock(&nic_data->wv_lock);
                return ENOENT;
        }
        /* The event handler is called after the filter was removed */
        nic_data->on_wol_virtue_remove(nic_data, virtue);
        fibril_rwlock_write_unlock(&nic_data->wv_lock);
        free(virtue->data);
        free(virtue);
        return EOK;
}

/**
 * Default implementation of the wol_virtue_probe method.
 * Queries the type and data of the virtue.
 *
 * @param[in]   fun
 * @param[in]   id              Virtue identifier
 * @param[out]  type            Type of the virtue. Can be NULL.
 * @param[out]  data            Data used when the virtue was created. Can be NULL.
 * @param[out]  length          Length of the data. Can be NULL.
 *
 * @return EOK          If the operation was successfully completed
 * @return ENOENT       If the virtue identifier is not valid.
 * @return ENOMEM       If there was not enough memory to complete the operation
 */
errno_t nic_wol_virtue_probe_impl(ddf_fun_t *fun, nic_wv_id_t id,
    nic_wv_type_t *type, size_t max_length, void *data, size_t *length)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->wv_lock);
        const nic_wol_virtue_t *virtue =
            nic_wol_virtues_find(&nic_data->wol_virtues, id);
        if (virtue == NULL) {
                *type = NIC_WV_NONE;
                *length = 0;
                fibril_rwlock_read_unlock(&nic_data->wv_lock);
                return ENOENT;
        } else {
                *type = virtue->type;
                if (max_length > virtue->length) {
                        max_length = virtue->length;
                }
                memcpy(data, virtue->data, max_length);
                *length = virtue->length;
                fibril_rwlock_read_unlock(&nic_data->wv_lock);
                return EOK;
        }
}

/**
 * Default implementation of the wol_virtue_list method.
 * List filters of the specified type. If NIC_WV_NONE is the type, it lists all
 * filters.
 *
 * @param[in]   fun
 * @param[in]   type    Type of the virtues
 * @param[out]  virtues Vector of virtue ID's.
 * @param[out]  count   Length of the data. Can be NULL.
 *
 * @return EOK          If the operation was successfully completed
 * @return ENOENT       If the filter identification is not valid.
 * @return ENOMEM       If there was not enough memory to complete the operation
 */
errno_t nic_wol_virtue_list_impl(ddf_fun_t *fun, nic_wv_type_t type,
    size_t max_count, nic_wv_id_t *id_list, size_t *id_count)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->wv_lock);
        errno_t rc = nic_wol_virtues_list(&nic_data->wol_virtues, type,
            max_count, id_list, id_count);
        fibril_rwlock_read_unlock(&nic_data->wv_lock);
        return rc;
}

/**
 * Default implementation of the wol_virtue_get_caps method.
 * Queries for the current capabilities for some type of filter.
 *
 * @param[in]   fun
 * @param[in]   type    Type of the virtues
 * @param[out]  count   Number of virtues of this type that can be currently set
 *
 * @return EOK          If the operation was successfully completed
 */
errno_t nic_wol_virtue_get_caps_impl(ddf_fun_t *fun, nic_wv_type_t type, int *count)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->wv_lock);
        *count = nic_data->wol_virtues.caps_max[type] -
            (int) nic_data->wol_virtues.lists_sizes[type];
        fibril_rwlock_read_unlock(&nic_data->wv_lock);
        return EOK;
}

/**
 * Default implementation of the poll_get_mode method.
 * Queries the current interrupt/poll mode of the NIC
 *
 * @param[in]   fun
 * @param[out]  mode            Current poll mode
 * @param[out]  period          Period used in periodic polling. Can be NULL.
 *
 * @return EOK          If the operation was successfully completed
 * @return ENOTSUP      This function is not supported.
 * @return EPARTY       Error in communication protocol
 */
errno_t nic_poll_get_mode_impl(ddf_fun_t *fun,
    nic_poll_mode_t *mode, struct timespec *period)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->main_lock);
        *mode = nic_data->poll_mode;
        memcpy(period, &nic_data->poll_period, sizeof(struct timespec));
        fibril_rwlock_read_unlock(&nic_data->main_lock);
        return EOK;
}

/**
 * Default implementation of the poll_set_mode_impl method.
 * Sets the interrupt/poll mode of the NIC.
 *
 * @param[in]   fun
 * @param[in]   mode            The new poll mode
 * @param[in]   period          Period used in periodic polling. Can be NULL.
 *
 * @return EOK          If the operation was successfully completed
 * @return ENOTSUP      This operation is not supported.
 * @return EPARTY       Error in communication protocol
 */
errno_t nic_poll_set_mode_impl(ddf_fun_t *fun,
    nic_poll_mode_t mode, const struct timespec *period)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        /*
         * If the driver does not implement the poll mode change handler it cannot
         * switch off interrupts and this is not supported.
         */
        if (nic_data->on_poll_mode_change == NULL)
                return ENOTSUP;

        if ((mode == NIC_POLL_ON_DEMAND) && nic_data->on_poll_request == NULL)
                return ENOTSUP;

        if (mode == NIC_POLL_PERIODIC || mode == NIC_POLL_SOFTWARE_PERIODIC) {
                if (period == NULL)
                        return EINVAL;
                if (period->tv_sec == 0 && period->tv_nsec == 0)
                        return EINVAL;
                if (period->tv_sec < 0 || period->tv_nsec < 0)
                        return EINVAL;
        }
        fibril_rwlock_write_lock(&nic_data->main_lock);
        errno_t rc = nic_data->on_poll_mode_change(nic_data, mode, period);
        assert(rc == EOK || rc == ENOTSUP || rc == EINVAL);
        if (rc == ENOTSUP && (nic_data->on_poll_request != NULL) &&
            (mode == NIC_POLL_PERIODIC || mode == NIC_POLL_SOFTWARE_PERIODIC)) {

                rc = nic_data->on_poll_mode_change(nic_data, NIC_POLL_ON_DEMAND, NULL);
                assert(rc == EOK || rc == ENOTSUP);
                if (rc == EOK)
                        nic_sw_period_start(nic_data);
        }
        if (rc == EOK) {
                nic_data->poll_mode = mode;
                if (period)
                        nic_data->poll_period = *period;
        }
        fibril_rwlock_write_unlock(&nic_data->main_lock);
        return rc;
}

/**
 * Default implementation of the poll_now method.
 * Wrapper for the actual poll implementation.
 *
 * @param[in]   fun
 *
 * @return EOK          If the NIC was polled
 * @return ENOTSUP      If the function is not supported
 * @return EINVAL       If the NIC is not in state where it allows on demand polling
 */
errno_t nic_poll_now_impl(ddf_fun_t *fun)
{
        nic_t *nic_data = nic_get_from_ddf_fun(fun);
        fibril_rwlock_read_lock(&nic_data->main_lock);
        if (nic_data->poll_mode != NIC_POLL_ON_DEMAND) {
                fibril_rwlock_read_unlock(&nic_data->main_lock);
                return EINVAL;
        }
        if (nic_data->on_poll_request != NULL) {
                nic_data->on_poll_request(nic_data);
                fibril_rwlock_read_unlock(&nic_data->main_lock);
                return EOK;
        } else {
                fibril_rwlock_read_unlock(&nic_data->main_lock);
                return ENOTSUP;
        }
}

/**
 * Default handler for unknown methods (outside of the NIC interface).
 * Logs a warning message and returns ENOTSUP to the caller.
 *
 * @param fun  The DDF function where the method should be called.
 * @param call IPC call data
 *
 */
void nic_default_handler_impl(ddf_fun_t *fun, ipc_call_t *call)
{
        async_answer_0(call, ENOTSUP);
}

/**
 * Default (empty) OPEN function implementation.
 *
 * @param fun   The DDF function
 *
 * @return EOK always.
 */
errno_t nic_open_impl(ddf_fun_t *fun)
{
        return EOK;
}

/**
 * Default (empty) OPEN function implementation.
 *
 * @param fun   The DDF function
 */
void nic_close_impl(ddf_fun_t *fun)
{
}

/** @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub