#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bitstring.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/iflib.h>
#include "aq_device.h"
#include "aq_fw.h"
#include "aq_dbg.h"
#define AQ_HW_SUPPORT_SPEED(softc, s) ((softc)->link_speeds & s)
void
aq_mediastatus_update(aq_dev_t *aq_dev, uint32_t link_speed,
const struct aq_hw_fc_info *fc_neg)
{
struct aq_hw *hw = &aq_dev->hw;
aq_dev->media_active = 0;
if (fc_neg->fc_rx)
aq_dev->media_active |= IFM_ETH_RXPAUSE;
if (fc_neg->fc_tx)
aq_dev->media_active |= IFM_ETH_TXPAUSE;
switch(link_speed) {
case 100:
aq_dev->media_active |= IFM_100_TX | IFM_FDX;
break;
case 1000:
aq_dev->media_active |= IFM_1000_T | IFM_FDX;
break;
case 2500:
aq_dev->media_active |= IFM_2500_T | IFM_FDX;
break;
case 5000:
aq_dev->media_active |= IFM_5000_T | IFM_FDX;
break;
case 10000:
aq_dev->media_active |= IFM_10G_T | IFM_FDX;
break;
case 0:
default:
aq_dev->media_active |= IFM_NONE;
break;
}
if (hw->link_rate == aq_fw_speed_auto)
aq_dev->media_active |= IFM_AUTO;
}
void
aq_mediastatus(if_t ifp, struct ifmediareq *ifmr)
{
aq_dev_t *aq_dev = iflib_get_softc(if_getsoftc(ifp));
ifmr->ifm_active = IFM_ETHER;
ifmr->ifm_status = IFM_AVALID;
if (aq_dev->linkup)
ifmr->ifm_status |= IFM_ACTIVE;
ifmr->ifm_active |= aq_dev->media_active;
}
int
aq_mediachange(if_t ifp)
{
aq_dev_t *aq_dev = iflib_get_softc(if_getsoftc(ifp));
struct aq_hw *hw = &aq_dev->hw;
int old_media_rate = if_getbaudrate(ifp);
int old_link_speed = hw->link_rate;
struct ifmedia *ifm = iflib_get_media(aq_dev->ctx);
int user_media = IFM_SUBTYPE(ifm->ifm_media);
uint64_t media_rate;
AQ_DBG_ENTERA("media 0x%x", user_media);
if (!(ifm->ifm_media & IFM_ETHER)) {
device_printf(aq_dev->dev,
"%s(): aq_dev interface - bad media: 0x%X", __FUNCTION__,
ifm->ifm_media);
return (0);
}
switch (user_media) {
case IFM_AUTO:
hw->link_rate = aq_fw_speed_auto;
media_rate = -1;
break;
case IFM_NONE:
media_rate = 0;
hw->link_rate = 0;
iflib_link_state_change(aq_dev->ctx, LINK_STATE_DOWN, 0);
break;
case IFM_100_TX:
hw->link_rate = aq_fw_100M;
media_rate = 100 * 1000;
break;
case IFM_1000_T:
hw->link_rate = aq_fw_1G;
media_rate = 1000 * 1000;
break;
case IFM_2500_T:
hw->link_rate = aq_fw_2G5;
media_rate = 2500 * 1000;
break;
case IFM_5000_T:
hw->link_rate = aq_fw_5G;
media_rate = 5000 * 1000;
break;
case IFM_10G_T:
hw->link_rate = aq_fw_10G;
media_rate = 10000 * 1000;
break;
default:
aq_log_error("unknown media: 0x%X", user_media);
return (0);
}
hw->fc.fc_rx = (ifm->ifm_media & IFM_ETH_RXPAUSE) ? 1 : 0;
hw->fc.fc_tx = (ifm->ifm_media & IFM_ETH_TXPAUSE) ? 1 : 0;
if (!(if_getflags(ifp) & IFF_UP))
return (0);
if ((media_rate != old_media_rate) ||
(hw->link_rate != old_link_speed)) {
aq_hw_set_link_speed(hw, hw->link_rate);
}
AQ_DBG_EXIT(0);
return (0);
}
static void
aq_add_media_types(aq_dev_t *aq_dev, int media_link_speed)
{
ifmedia_add(aq_dev->media, IFM_ETHER | media_link_speed | IFM_FDX, 0,
NULL);
ifmedia_add(aq_dev->media, IFM_ETHER | media_link_speed | IFM_FDX |
IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE, 0, NULL);
ifmedia_add(aq_dev->media, IFM_ETHER | media_link_speed | IFM_FDX |
IFM_ETH_RXPAUSE, 0, NULL);
ifmedia_add(aq_dev->media, IFM_ETHER | media_link_speed | IFM_FDX |
IFM_ETH_TXPAUSE, 0, NULL);
}
void
aq_initmedia(aq_dev_t *aq_dev)
{
AQ_DBG_ENTER();
ifmedia_add(aq_dev->media, IFM_ETHER | IFM_NONE, 0, NULL);
aq_add_media_types(aq_dev, IFM_AUTO);
if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_100M))
aq_add_media_types(aq_dev, IFM_100_TX);
if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_1G))
aq_add_media_types(aq_dev, IFM_1000_T);
if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_2G5))
aq_add_media_types(aq_dev, IFM_2500_T);
if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_5G))
aq_add_media_types(aq_dev, IFM_5000_T);
if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_10G))
aq_add_media_types(aq_dev, IFM_10G_T);
ifmedia_set(aq_dev->media,
IFM_ETHER | IFM_AUTO | IFM_FDX | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE);
AQ_DBG_EXIT(0);
}