/*
* 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)
{
}
/** @}
*/