summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/drivers/net
diff options
context:
space:
mode:
authorDon Dugger <n0ano@n0ano.com>2016-06-03 03:33:22 +0000
committerGerrit Code Review <gerrit@172.30.200.206>2016-06-03 03:33:23 +0000
commitda27230f80795d0028333713f036d44c53cb0e68 (patch)
treeb3d379eaf000adf72b36cb01cdf4d79c3e3f064c /qemu/roms/ipxe/src/drivers/net
parent0e68cb048bb8aadb14675f5d4286d8ab2fc35449 (diff)
parent437fd90c0250dee670290f9b714253671a990160 (diff)
Merge "These changes are the raw update to qemu-2.6."
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/net')
-rw-r--r--qemu/roms/ipxe/src/drivers/net/amd8111e.h6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c16
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c18
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c12
-rw-r--r--qemu/roms/ipxe/src/drivers/net/atl1e.c2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/davicom.c1
-rw-r--r--qemu/roms/ipxe/src/drivers/net/dm96xx.c671
-rw-r--r--qemu/roms/ipxe/src/drivers/net/dm96xx.h194
-rw-r--r--qemu/roms/ipxe/src/drivers/net/dmfe.c2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ecm.c520
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ecm.h93
-rw-r--r--qemu/roms/ipxe/src/drivers/net/eepro.c20
-rw-r--r--qemu/roms/ipxe/src/drivers/net/eepro100.c1
-rw-r--r--qemu/roms/ipxe/src/drivers/net/efi/nii.c150
-rw-r--r--qemu/roms/ipxe/src/drivers/net/efi/nii.h2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/efi/snp.c6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/efi/snponly.c6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/etherfabric.c2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/forcedeth.c6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intel.c148
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intel.h123
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intelvf.c340
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intelvf.h109
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intelx.c25
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intelx.h2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intelxvf.c466
-rw-r--r--qemu/roms/ipxe/src/drivers/net/intelxvf.h104
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ipoib.c90
-rw-r--r--qemu/roms/ipxe/src/drivers/net/legacy.c2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/mii.c38
-rw-r--r--qemu/roms/ipxe/src/drivers/net/myson.c6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/myson.h2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ncm.c672
-rw-r--r--qemu/roms/ipxe/src/drivers/net/ncm.h173
-rw-r--r--qemu/roms/ipxe/src/drivers/net/netfront.c16
-rw-r--r--qemu/roms/ipxe/src/drivers/net/netfront.h2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/netvsc.c848
-rw-r--r--qemu/roms/ipxe/src/drivers/net/netvsc.h365
-rw-r--r--qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/phantom/phantom.c6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/phantom/phantom.h6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/pnic.c14
-rw-r--r--qemu/roms/ipxe/src/drivers/net/prism2.c14
-rw-r--r--qemu/roms/ipxe/src/drivers/net/prism2_pci.c16
-rw-r--r--qemu/roms/ipxe/src/drivers/net/prism2_plx.c18
-rw-r--r--qemu/roms/ipxe/src/drivers/net/realtek.c11
-rw-r--r--qemu/roms/ipxe/src/drivers/net/realtek.h2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c20
-rw-r--r--qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c14
-rw-r--r--qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c26
-rw-r--r--qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h4
-rw-r--r--qemu/roms/ipxe/src/drivers/net/skeleton.c6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/skeleton.h2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/smsc75xx.c1057
-rw-r--r--qemu/roms/ipxe/src/drivers/net/smsc75xx.h309
-rw-r--r--qemu/roms/ipxe/src/drivers/net/sundance.c2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/tg3/tg3.c1
-rw-r--r--qemu/roms/ipxe/src/drivers/net/tg3/tg3.h5
-rw-r--r--qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c1
-rw-r--r--qemu/roms/ipxe/src/drivers/net/virtio-net.c14
-rw-r--r--qemu/roms/ipxe/src/drivers/net/vmxnet3.c6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/vmxnet3.h6
-rw-r--r--qemu/roms/ipxe/src/drivers/net/vxge/vxge.c3
-rw-r--r--qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c2
-rw-r--r--qemu/roms/ipxe/src/drivers/net/w89c840.c2
68 files changed, 6602 insertions, 240 deletions
diff --git a/qemu/roms/ipxe/src/drivers/net/amd8111e.h b/qemu/roms/ipxe/src/drivers/net/amd8111e.h
index 2000df158..8ecd159af 100644
--- a/qemu/roms/ipxe/src/drivers/net/amd8111e.h
+++ b/qemu/roms/ipxe/src/drivers/net/amd8111e.h
@@ -16,6 +16,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
* USA
Module Name:
@@ -36,7 +40,7 @@ Revision History:
3.0.1
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifndef _AMD811E_H
#define _AMD811E_H
diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h
index dbd4d4d5b..ba87ba0fd 100644
--- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h
+++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ani.h
@@ -125,7 +125,7 @@ struct ar5416AniState {
u8 mrcCCKOff;
u8 spurImmunityLevel;
u8 firstepLevel;
- u8 ofdmWeakSigDetectOff;
+ u8 ofdmWeakSigDetect;
u8 cckWeakSigThreshold;
u32 listenTime;
int32_t rssiThrLow;
diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c
index ff7df497f..76ca79cba 100644
--- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c
+++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c
@@ -177,7 +177,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah)
rssi = BEACON_RSSI(ah);
if (rssi > aniState->rssiThrHigh) {
- if (!aniState->ofdmWeakSigDetectOff) {
+ if (aniState->ofdmWeakSigDetect) {
if (ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
0)) {
@@ -192,7 +192,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah)
return;
}
} else if (rssi > aniState->rssiThrLow) {
- if (aniState->ofdmWeakSigDetectOff)
+ if (!aniState->ofdmWeakSigDetect)
ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
1);
@@ -202,7 +202,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah)
return;
} else {
if ((ah->dev->channels + ah->dev->channel)->band == NET80211_BAND_2GHZ) {
- if (!aniState->ofdmWeakSigDetectOff)
+ if (aniState->ofdmWeakSigDetect)
ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
0);
@@ -360,7 +360,7 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah)
if (rssi > aniState->rssiThrHigh) {
/* XXX: Handle me */
} else if (rssi > aniState->rssiThrLow) {
- if (aniState->ofdmWeakSigDetectOff) {
+ if (!aniState->ofdmWeakSigDetect) {
if (ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
1) == 1)
@@ -436,9 +436,9 @@ static void ath9k_ani_reset_old(struct ath_hw *ah)
if (aniState->spurImmunityLevel != 0)
ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
aniState->spurImmunityLevel);
- if (aniState->ofdmWeakSigDetectOff)
+ if (!aniState->ofdmWeakSigDetect)
ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- !aniState->ofdmWeakSigDetectOff);
+ aniState->ofdmWeakSigDetect);
if (aniState->cckWeakSigThreshold)
ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
aniState->cckWeakSigThreshold);
@@ -709,8 +709,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
- ani->ofdmWeakSigDetectOff =
- !ATH9K_ANI_USE_OFDM_WEAK_SIG;
+ ani->ofdmWeakSigDetect =
+ ATH9K_ANI_USE_OFDM_WEAK_SIG;
ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
}
diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c
index 60e87e9e2..2b6c133cb 100644
--- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c
+++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c
@@ -1141,12 +1141,12 @@ static int ar5008_hw_ani_control_old(struct ath_hw *ah,
REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
- if (!on != aniState->ofdmWeakSigDetectOff) {
+ if (on != aniState->ofdmWeakSigDetect) {
if (on)
ah->stats.ast_ani_ofdmon++;
else
ah->stats.ast_ani_ofdmoff++;
- aniState->ofdmWeakSigDetectOff = !on;
+ aniState->ofdmWeakSigDetect = on;
}
break;
}
@@ -1215,10 +1215,10 @@ static int ar5008_hw_ani_control_old(struct ath_hw *ah,
DBG2("ath9k: ANI parameters:\n");
DBG2(
- "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetectOff=%d\n",
+ "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetect=%d\n",
aniState->noiseImmunityLevel,
aniState->spurImmunityLevel,
- !aniState->ofdmWeakSigDetectOff);
+ aniState->ofdmWeakSigDetect);
DBG2(
"cckWeakSigThreshold=%d, firstepLevel=%d, listenTime=%d\n",
aniState->cckWeakSigThreshold,
@@ -1307,18 +1307,18 @@ static int ar5008_hw_ani_control_new(struct ath_hw *ah,
REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
- if (!on != aniState->ofdmWeakSigDetectOff) {
+ if (on != aniState->ofdmWeakSigDetect) {
DBG2("ath9k: "
"** ch %d: ofdm weak signal: %s=>%s\n",
chan->channel,
- !aniState->ofdmWeakSigDetectOff ?
+ aniState->ofdmWeakSigDetect ?
"on" : "off",
on ? "on" : "off");
if (on)
ah->stats.ast_ani_ofdmon++;
else
ah->stats.ast_ani_ofdmoff++;
- aniState->ofdmWeakSigDetectOff = !on;
+ aniState->ofdmWeakSigDetect = on;
}
break;
}
@@ -1467,7 +1467,7 @@ static int ar5008_hw_ani_control_new(struct ath_hw *ah,
DBG2("ath9k: "
"ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n",
aniState->spurImmunityLevel,
- !aniState->ofdmWeakSigDetectOff ? "on" : "off",
+ aniState->ofdmWeakSigDetect ? "on" : "off",
aniState->firstepLevel,
!aniState->mrcCCKOff ? "on" : "off",
aniState->listenTime,
@@ -1554,7 +1554,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
/* these levels just got reset to defaults by the INI */
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW;
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW;
- aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG;
+ aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
aniState->mrcCCKOff = 1; /* not available on pre AR9003 */
}
diff --git a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c
index 6103040ab..2244b775a 100644
--- a/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c
+++ b/qemu/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c
@@ -859,18 +859,18 @@ static int ar9003_hw_ani_control(struct ath_hw *ah,
REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
- if (!on != aniState->ofdmWeakSigDetectOff) {
+ if (on != aniState->ofdmWeakSigDetect) {
DBG2("ath9k: "
"** ch %d: ofdm weak signal: %s=>%s\n",
chan->channel,
- !aniState->ofdmWeakSigDetectOff ?
+ aniState->ofdmWeakSigDetect ?
"on" : "off",
on ? "on" : "off");
if (on)
ah->stats.ast_ani_ofdmon++;
else
ah->stats.ast_ani_ofdmoff++;
- aniState->ofdmWeakSigDetectOff = !on;
+ aniState->ofdmWeakSigDetect = on;
}
break;
}
@@ -1013,7 +1013,7 @@ static int ar9003_hw_ani_control(struct ath_hw *ah,
AR_PHY_MRC_CCK_ENABLE, is_on);
REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
AR_PHY_MRC_CCK_MUX_REG, is_on);
- if (!is_on != aniState->mrcCCKOff) {
+ if (!(is_on != aniState->mrcCCKOff)) {
DBG2("ath9k: "
"** ch %d: MRC CCK: %s=>%s\n",
chan->channel,
@@ -1037,7 +1037,7 @@ static int ar9003_hw_ani_control(struct ath_hw *ah,
DBG2("ath9k: "
"ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n",
aniState->spurImmunityLevel,
- !aniState->ofdmWeakSigDetectOff ? "on" : "off",
+ aniState->ofdmWeakSigDetect ? "on" : "off",
aniState->firstepLevel,
!aniState->mrcCCKOff ? "on" : "off",
aniState->listenTime,
@@ -1137,7 +1137,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
/* these levels just got reset to defaults by the INI */
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW;
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW;
- aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG;
+ aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK;
}
diff --git a/qemu/roms/ipxe/src/drivers/net/atl1e.c b/qemu/roms/ipxe/src/drivers/net/atl1e.c
index 1ff0f0d10..d010d8c4a 100644
--- a/qemu/roms/ipxe/src/drivers/net/atl1e.c
+++ b/qemu/roms/ipxe/src/drivers/net/atl1e.c
@@ -224,7 +224,7 @@ static int atl1e_sw_init(struct atl1e_adapter *adapter)
adapter->link_duplex = FULL_DUPLEX;
/* PCI config space info */
- pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
+ pci_read_config_byte(pdev, PCI_REVISION, &rev_id);
phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS);
/* nic type */
diff --git a/qemu/roms/ipxe/src/drivers/net/davicom.c b/qemu/roms/ipxe/src/drivers/net/davicom.c
index a4870a729..9d3d8b915 100644
--- a/qemu/roms/ipxe/src/drivers/net/davicom.c
+++ b/qemu/roms/ipxe/src/drivers/net/davicom.c
@@ -340,6 +340,7 @@ static void davicom_media_chk(struct nic * nic __unused)
csr6 = 0x00200000; /* SF */
outl(csr6, ioaddr + CSR6);
+#define PCI_VENDOR_ID_DAVICOM 0x1282
#define PCI_DEVICE_ID_DM9009 0x9009
if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9009) {
/* Set to 10BaseT mode for DM9009 */
diff --git a/qemu/roms/ipxe/src/drivers/net/dm96xx.c b/qemu/roms/ipxe/src/drivers/net/dm96xx.c
new file mode 100644
index 000000000..58d8dd964
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/dm96xx.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/usb.h>
+#include <ipxe/usbnet.h>
+#include "dm96xx.h"
+
+/** @file
+ *
+ * Davicom DM96xx USB Ethernet driver
+ *
+ */
+
+/******************************************************************************
+ *
+ * Register operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Reset device
+ *
+ * @v dm96xx DM96xx device
+ * @ret rc Return status code
+ */
+static int dm96xx_reset ( struct dm96xx_device *dm96xx ) {
+ int ncr;
+ int rc;
+
+ /* Reset device */
+ if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_NCR,
+ DM96XX_NCR_RST ) ) != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p could not reset: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Wait for reset to complete */
+ udelay ( DM96XX_RESET_DELAY_US );
+
+ /* Check that reset has completed */
+ ncr = dm96xx_read_register ( dm96xx, DM96XX_NCR );
+ if ( ncr < 0 ) {
+ rc = ncr;
+ DBGC ( dm96xx, "DM96XX %p failed to reset: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+ if ( ncr & DM96XX_NCR_RST ) {
+ DBGC ( dm96xx, "DM96XX %p failed to reset (NCR=%#02x)\n",
+ dm96xx, ncr );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Read MAC address
+ *
+ * @v dm96xx DM96xx device
+ * @v mac MAC address to fill in
+ * @ret rc Return status code
+ */
+static int dm96xx_read_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) {
+ int rc;
+
+ /* Read MAC address */
+ if ( ( rc = dm96xx_read_registers ( dm96xx, DM96XX_PAR, mac,
+ ETH_ALEN ) ) != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p could not read MAC address: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Write MAC address
+ *
+ * @v dm96xx DM96xx device
+ * @v mac MAC address
+ * @ret rc Return status code
+ */
+static int dm96xx_write_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) {
+ int rc;
+
+ /* Write MAC address */
+ if ( ( rc = dm96xx_write_registers ( dm96xx, DM96XX_PAR, mac,
+ ETH_ALEN ) ) != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p could not write MAC address: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Update link status based on network status register
+ *
+ * @v dm96xx DM96xx device
+ * @v nsr Network status register
+ */
+static void dm96xx_link_nsr ( struct dm96xx_device *dm96xx, unsigned int nsr ) {
+ struct net_device *netdev = dm96xx->netdev;
+
+ if ( nsr & DM96XX_NSR_LINKST ) {
+ if ( ! netdev_link_ok ( netdev ) )
+ netdev_link_up ( netdev );
+ } else {
+ if ( netdev_link_ok ( netdev ) )
+ netdev_link_down ( netdev );
+ }
+}
+
+/**
+ * Get link status
+ *
+ * @v dm96xx DM96xx device
+ * @ret rc Return status code
+ */
+static int dm96xx_check_link ( struct dm96xx_device *dm96xx ) {
+ int nsr;
+ int rc;
+
+ /* Read network status register */
+ nsr = dm96xx_read_register ( dm96xx, DM96XX_NSR );
+ if ( nsr < 0 ) {
+ rc = nsr;
+ DBGC ( dm96xx, "DM96XX %p could not read network status: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Update link status */
+ dm96xx_link_nsr ( dm96xx, nsr );
+
+ return 0;
+}
+
+/**
+ * Set DM9601-compatible RX header mode
+ *
+ * @v dm96xx DM96xx device
+ * @ret rc Return status code
+ */
+static int dm96xx_rx_mode ( struct dm96xx_device *dm96xx ) {
+ int chipr;
+ int mode_ctl;
+ int rc;
+
+ /* Get chip revision */
+ chipr = dm96xx_read_register ( dm96xx, DM96XX_CHIPR );
+ if ( chipr < 0 ) {
+ rc = chipr;
+ DBGC ( dm96xx, "DM96XX %p could not read chip revision: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Do nothing if device is a DM9601 anyway */
+ if ( chipr == DM96XX_CHIPR_9601 )
+ return 0;
+
+ /* Read current mode control */
+ mode_ctl = dm96xx_read_register ( dm96xx, DM96XX_MODE_CTL );
+ if ( mode_ctl < 0 ) {
+ rc = mode_ctl;
+ DBGC ( dm96xx, "DM96XX %p could not read mode control: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Write mode control */
+ mode_ctl &= ~DM96XX_MODE_CTL_MODE;
+ if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_MODE_CTL,
+ mode_ctl ) ) != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p could not write mode control: %s\n",
+ dm96xx, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Endpoint operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Complete interrupt transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void dm96xx_intr_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device,
+ usbnet.intr );
+ struct net_device *netdev = dm96xx->netdev;
+ struct dm96xx_interrupt *intr;
+ size_t len = iob_len ( iobuf );
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open )
+ goto done;
+
+ /* Record USB errors against the network device */
+ if ( rc != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p interrupt failed: %s\n",
+ dm96xx, strerror ( rc ) );
+ DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) );
+ netdev_rx_err ( netdev, NULL, rc );
+ goto done;
+ }
+
+ /* Extract message header */
+ if ( len < sizeof ( *intr ) ) {
+ DBGC ( dm96xx, "DM96XX %p underlength interrupt:\n", dm96xx );
+ DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) );
+ netdev_rx_err ( netdev, NULL, -EINVAL );
+ goto done;
+ }
+ intr = iobuf->data;
+
+ /* Update link status */
+ dm96xx_link_nsr ( dm96xx, intr->nsr );
+
+ done:
+ /* Free I/O buffer */
+ free_iob ( iobuf );
+}
+
+/** Interrupt endpoint operations */
+static struct usb_endpoint_driver_operations dm96xx_intr_operations = {
+ .complete = dm96xx_intr_complete,
+};
+
+/**
+ * Complete bulk IN transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void dm96xx_in_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device,
+ usbnet.in );
+ struct net_device *netdev = dm96xx->netdev;
+ struct dm96xx_rx_header *header;
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open ) {
+ free_iob ( iobuf );
+ return;
+ }
+
+ /* Record USB errors against the network device */
+ if ( rc != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p bulk IN failed: %s\n",
+ dm96xx, strerror ( rc ) );
+ goto err;
+ }
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) {
+ DBGC ( dm96xx, "DM96XX %p underlength bulk IN\n", dm96xx );
+ DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* Strip header and CRC */
+ header = iobuf->data;
+ iob_pull ( iobuf, sizeof ( *header ) );
+ iob_unput ( iobuf, 4 /* CRC */ );
+
+ /* Check status */
+ if ( header->rsr & ~DM96XX_RSR_MF ) {
+ DBGC ( dm96xx, "DM96XX %p receive error %02x:\n",
+ dm96xx, header->rsr );
+ DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -EIO;
+ goto err;
+ }
+
+ /* Hand off to network stack */
+ netdev_rx ( netdev, iob_disown ( iobuf ) );
+ return;
+
+ err:
+ /* Hand off to network stack */
+ netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
+}
+
+/** Bulk IN endpoint operations */
+static struct usb_endpoint_driver_operations dm96xx_in_operations = {
+ .complete = dm96xx_in_complete,
+};
+
+/**
+ * Transmit packet
+ *
+ * @v dm96xx DM96xx device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int dm96xx_out_transmit ( struct dm96xx_device *dm96xx,
+ struct io_buffer *iobuf ) {
+ struct dm96xx_tx_header *header;
+ size_t len = iob_len ( iobuf );
+ int rc;
+
+ /* Prepend header */
+ if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 )
+ return rc;
+ header = iob_push ( iobuf, sizeof ( *header ) );
+ header->len = cpu_to_le16 ( len );
+
+ /* Enqueue I/O buffer */
+ if ( ( rc = usb_stream ( &dm96xx->usbnet.out, iobuf, 0 ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Complete bulk OUT transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void dm96xx_out_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device,
+ usbnet.out );
+ struct net_device *netdev = dm96xx->netdev;
+
+ /* Report TX completion */
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+}
+
+/** Bulk OUT endpoint operations */
+static struct usb_endpoint_driver_operations dm96xx_out_operations = {
+ .complete = dm96xx_out_complete,
+};
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int dm96xx_open ( struct net_device *netdev ) {
+ struct dm96xx_device *dm96xx = netdev->priv;
+ unsigned int rcr;
+ int rc;
+
+ /* Set DM9601-compatible RX header mode */
+ if ( ( rc = dm96xx_rx_mode ( dm96xx ) ) != 0 )
+ goto err_rx_mode;
+
+ /* Write MAC address */
+ if ( ( rc = dm96xx_write_mac ( dm96xx, netdev->ll_addr ) ) != 0 )
+ goto err_write_mac;
+
+ /* Open USB network device */
+ if ( ( rc = usbnet_open ( &dm96xx->usbnet ) ) != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p could not open: %s\n",
+ dm96xx, strerror ( rc ) );
+ goto err_open;
+ }
+
+ /* Set receive filters */
+ rcr = ( DM96XX_RCR_ALL | DM96XX_RCR_RUNT | DM96XX_RCR_PRMSC |
+ DM96XX_RCR_RXEN );
+ if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_RCR, rcr ) ) != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p could not write receive filters: "
+ "%s\n", dm96xx, strerror ( rc ) );
+ goto err_write_rcr;
+ }
+
+ /* Update link status */
+ if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 )
+ goto err_check_link;
+
+ return 0;
+
+ err_check_link:
+ err_write_rcr:
+ usbnet_close ( &dm96xx->usbnet );
+ err_open:
+ err_write_mac:
+ err_rx_mode:
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void dm96xx_close ( struct net_device *netdev ) {
+ struct dm96xx_device *dm96xx = netdev->priv;
+
+ /* Close USB network device */
+ usbnet_close ( &dm96xx->usbnet );
+
+ /* Reset device */
+ dm96xx_reset ( dm96xx );
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int dm96xx_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct dm96xx_device *dm96xx = netdev->priv;
+ int rc;
+
+ /* Transmit packet */
+ if ( ( rc = dm96xx_out_transmit ( dm96xx, iobuf ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v netdev Network device
+ */
+static void dm96xx_poll ( struct net_device *netdev ) {
+ struct dm96xx_device *dm96xx = netdev->priv;
+ int rc;
+
+ /* Poll USB bus */
+ usb_poll ( dm96xx->bus );
+
+ /* Refill endpoints */
+ if ( ( rc = usbnet_refill ( &dm96xx->usbnet ) ) != 0 )
+ netdev_rx_err ( netdev, NULL, rc );
+}
+
+/** DM96xx network device operations */
+static struct net_device_operations dm96xx_operations = {
+ .open = dm96xx_open,
+ .close = dm96xx_close,
+ .transmit = dm96xx_transmit,
+ .poll = dm96xx_poll,
+};
+
+/******************************************************************************
+ *
+ * USB interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe device
+ *
+ * @v func USB function
+ * @v config Configuration descriptor
+ * @ret rc Return status code
+ */
+static int dm96xx_probe ( struct usb_function *func,
+ struct usb_configuration_descriptor *config ) {
+ struct usb_device *usb = func->usb;
+ struct net_device *netdev;
+ struct dm96xx_device *dm96xx;
+ int rc;
+
+ /* Allocate and initialise structure */
+ netdev = alloc_etherdev ( sizeof ( *dm96xx ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &dm96xx_operations );
+ netdev->dev = &func->dev;
+ dm96xx = netdev->priv;
+ memset ( dm96xx, 0, sizeof ( *dm96xx ) );
+ dm96xx->usb = usb;
+ dm96xx->bus = usb->port->hub->bus;
+ dm96xx->netdev = netdev;
+ usbnet_init ( &dm96xx->usbnet, func, &dm96xx_intr_operations,
+ &dm96xx_in_operations, &dm96xx_out_operations );
+ usb_refill_init ( &dm96xx->usbnet.intr, 0, DM96XX_INTR_MAX_FILL );
+ usb_refill_init ( &dm96xx->usbnet.in, DM96XX_IN_MTU,
+ DM96XX_IN_MAX_FILL );
+ DBGC ( dm96xx, "DM96XX %p on %s\n", dm96xx, func->name );
+
+ /* Describe USB network device */
+ if ( ( rc = usbnet_describe ( &dm96xx->usbnet, config ) ) != 0 ) {
+ DBGC ( dm96xx, "DM96XX %p could not describe: %s\n",
+ dm96xx, strerror ( rc ) );
+ goto err_describe;
+ }
+
+ /* Reset device */
+ if ( ( rc = dm96xx_reset ( dm96xx ) ) != 0 )
+ goto err_reset;
+
+ /* Read MAC address */
+ if ( ( rc = dm96xx_read_mac ( dm96xx, netdev->hw_addr ) ) != 0 )
+ goto err_read_mac;
+
+ /* Get initial link status */
+ if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 )
+ goto err_check_link;
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+
+ usb_func_set_drvdata ( func, netdev );
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register:
+ err_check_link:
+ err_read_mac:
+ err_reset:
+ err_describe:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove device
+ *
+ * @v func USB function
+ */
+static void dm96xx_remove ( struct usb_function *func ) {
+ struct net_device *netdev = usb_func_get_drvdata ( func );
+
+ unregister_netdev ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** DM96xx device IDs */
+static struct usb_device_id dm96xx_ids[] = {
+ {
+ .name = "dm9601-corega",
+ .vendor = 0x07aa,
+ .product = 0x9601,
+ },
+ {
+ .name = "dm9601",
+ .vendor = 0x0a46,
+ .product = 0x9601,
+ },
+ {
+ .name = "zt6688",
+ .vendor = 0x0a46,
+ .product = 0x6688,
+ },
+ {
+ .name = "st268",
+ .vendor = 0x0a46,
+ .product = 0x0268,
+ },
+ {
+ .name = "adm8515",
+ .vendor = 0x0a46,
+ .product = 0x8515,
+ },
+ {
+ .name = "dm9601-hirose",
+ .vendor = 0x0a47,
+ .product = 0x9601,
+ },
+ {
+ .name = "dm9601-8101",
+ .vendor = 0x0fe6,
+ .product = 0x8101,
+ },
+ {
+ .name = "dm9601-9700",
+ .vendor = 0x0fe6,
+ .product = 0x9700,
+ },
+ {
+ .name = "dm9000e",
+ .vendor = 0x0a46,
+ .product = 0x9000,
+ },
+ {
+ .name = "dm9620",
+ .vendor = 0x0a46,
+ .product = 0x9620,
+ },
+ {
+ .name = "dm9621A",
+ .vendor = 0x0a46,
+ .product = 0x9621,
+ },
+ {
+ .name = "dm9622",
+ .vendor = 0x0a46,
+ .product = 0x9622,
+ },
+ {
+ .name = "dm962Oa",
+ .vendor = 0x0a46,
+ .product = 0x0269,
+ },
+ {
+ .name = "dm9621a",
+ .vendor = 0x0a46,
+ .product = 0x1269,
+ },
+};
+
+/** Davicom DM96xx driver */
+struct usb_driver dm96xx_driver __usb_driver = {
+ .ids = dm96xx_ids,
+ .id_count = ( sizeof ( dm96xx_ids ) / sizeof ( dm96xx_ids[0] ) ),
+ .probe = dm96xx_probe,
+ .remove = dm96xx_remove,
+};
diff --git a/qemu/roms/ipxe/src/drivers/net/dm96xx.h b/qemu/roms/ipxe/src/drivers/net/dm96xx.h
new file mode 100644
index 000000000..43a1a4e30
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/dm96xx.h
@@ -0,0 +1,194 @@
+#ifndef _DM96XX_H
+#define _DM96XX_H
+
+/** @file
+ *
+ * Davicom DM96xx USB Ethernet driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/usb.h>
+#include <ipxe/usbnet.h>
+#include <ipxe/if_ether.h>
+
+/** Read register(s) */
+#define DM96XX_READ_REGISTER \
+ ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \
+ USB_REQUEST_TYPE ( 0x00 ) )
+
+/** Write register(s) */
+#define DM96XX_WRITE_REGISTER \
+ ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \
+ USB_REQUEST_TYPE ( 0x01 ) )
+
+/** Write single register */
+#define DM96XX_WRITE1_REGISTER \
+ ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \
+ USB_REQUEST_TYPE ( 0x03 ) )
+
+/** Network control register */
+#define DM96XX_NCR 0x00
+#define DM96XX_NCR_RST 0x01 /**< Software reset */
+
+/** Network status register */
+#define DM96XX_NSR 0x01
+#define DM96XX_NSR_LINKST 0x40 /**< Link status */
+
+/** Receive control register */
+#define DM96XX_RCR 0x05
+#define DM96XX_RCR_ALL 0x08 /**< Pass all multicast */
+#define DM96XX_RCR_RUNT 0x04 /**< Pass runt packet */
+#define DM96XX_RCR_PRMSC 0x02 /**< Promiscuous mode */
+#define DM96XX_RCR_RXEN 0x01 /**< RX enable */
+
+/** Receive status register */
+#define DM96XX_RSR 0x06
+#define DM96XX_RSR_MF 0x40 /**< Multicast frame */
+
+/** PHY address registers */
+#define DM96XX_PAR 0x10
+
+/** Chip revision register */
+#define DM96XX_CHIPR 0x2c
+#define DM96XX_CHIPR_9601 0x00 /**< DM9601 */
+#define DM96XX_CHIPR_9620 0x01 /**< DM9620 */
+
+/** RX header control/status register (DM9620+ only) */
+#define DM96XX_MODE_CTL 0x91
+#define DM96XX_MODE_CTL_MODE 0x80 /**< 4-byte header mode */
+
+/** DM96xx interrupt data */
+struct dm96xx_interrupt {
+ /** Network status register */
+ uint8_t nsr;
+ /** Transmit status registers */
+ uint8_t tsr[2];
+ /** Receive status register */
+ uint8_t rsr;
+ /** Receive overflow counter register */
+ uint8_t rocr;
+ /** Receive packet counter */
+ uint8_t rxc;
+ /** Transmit packet counter */
+ uint8_t txc;
+ /** General purpose register */
+ uint8_t gpr;
+} __attribute__ (( packed ));
+
+/** DM96xx receive header */
+struct dm96xx_rx_header {
+ /** Packet status */
+ uint8_t rsr;
+ /** Packet length (excluding this header, including CRC) */
+ uint16_t len;
+} __attribute__ (( packed ));
+
+/** DM96xx transmit header */
+struct dm96xx_tx_header {
+ /** Packet length (excluding this header) */
+ uint16_t len;
+} __attribute__ (( packed ));
+
+/** A DM96xx network device */
+struct dm96xx_device {
+ /** USB device */
+ struct usb_device *usb;
+ /** USB bus */
+ struct usb_bus *bus;
+ /** Network device */
+ struct net_device *netdev;
+ /** USB network device */
+ struct usbnet_device usbnet;
+};
+
+/**
+ * Read registers
+ *
+ * @v dm96xx DM96xx device
+ * @v offset Register offset
+ * @v data Data buffer
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+dm96xx_read_registers ( struct dm96xx_device *dm96xx, unsigned int offset,
+ void *data, size_t len ) {
+
+ return usb_control ( dm96xx->usb, DM96XX_READ_REGISTER, 0, offset,
+ data, len );
+}
+
+/**
+ * Read register
+ *
+ * @v dm96xx DM96xx device
+ * @v offset Register offset
+ * @ret value Register value, or negative error
+ */
+static inline __attribute__ (( always_inline )) int
+dm96xx_read_register ( struct dm96xx_device *dm96xx, unsigned int offset ) {
+ uint8_t value;
+ int rc;
+
+ if ( ( rc = dm96xx_read_registers ( dm96xx, offset, &value,
+ sizeof ( value ) ) ) != 0 )
+ return rc;
+ return value;
+}
+
+/**
+ * Write registers
+ *
+ * @v dm96xx DM96xx device
+ * @v offset Register offset
+ * @v data Data buffer
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+dm96xx_write_registers ( struct dm96xx_device *dm96xx, unsigned int offset,
+ void *data, size_t len ) {
+
+ return usb_control ( dm96xx->usb, DM96XX_WRITE_REGISTER, 0, offset,
+ data, len );
+}
+
+/**
+ * Write register
+ *
+ * @v dm96xx DM96xx device
+ * @v offset Register offset
+ * @v value Register value
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+dm96xx_write_register ( struct dm96xx_device *dm96xx, unsigned int offset,
+ uint8_t value ) {
+
+ return usb_control ( dm96xx->usb, DM96XX_WRITE1_REGISTER, value,
+ offset, NULL, 0 );
+}
+
+/** Reset delay (in microseconds) */
+#define DM96XX_RESET_DELAY_US 10
+
+/** Interrupt maximum fill level
+ *
+ * This is a policy decision.
+ */
+#define DM96XX_INTR_MAX_FILL 2
+
+/** Bulk IN maximum fill level
+ *
+ * This is a policy decision.
+ */
+#define DM96XX_IN_MAX_FILL 8
+
+/** Bulk IN buffer size */
+#define DM96XX_IN_MTU \
+ ( 4 /* DM96xx header */ + ETH_FRAME_LEN + \
+ 4 /* possible VLAN header */ + 4 /* CRC */ )
+
+#endif /* _DM96XX_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/dmfe.c b/qemu/roms/ipxe/src/drivers/net/dmfe.c
index aae40fce7..2ea0d2b2b 100644
--- a/qemu/roms/ipxe/src/drivers/net/dmfe.c
+++ b/qemu/roms/ipxe/src/drivers/net/dmfe.c
@@ -462,7 +462,7 @@ static int dmfe_probe ( struct nic *nic, struct pci_device *pci ) {
pci->id->name, pci->vendor, pci->device);
/* Read Chip revision */
- pci_read_config_dword(pci, PCI_REVISION_ID, &dev_rev);
+ pci_read_config_dword(pci, PCI_REVISION, &dev_rev);
dprintf(("Revision %lX\n", dev_rev));
/* point to private storage */
diff --git a/qemu/roms/ipxe/src/drivers/net/ecm.c b/qemu/roms/ipxe/src/drivers/net/ecm.c
new file mode 100644
index 000000000..8c84ea9e9
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/ecm.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <errno.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/base16.h>
+#include <ipxe/profile.h>
+#include <ipxe/usb.h>
+#include "ecm.h"
+
+/** @file
+ *
+ * CDC-ECM USB Ethernet driver
+ *
+ */
+
+/** Interrupt completion profiler */
+static struct profiler ecm_intr_profiler __profiler =
+ { .name = "ecm.intr" };
+
+/** Bulk IN completion profiler */
+static struct profiler ecm_in_profiler __profiler =
+ { .name = "ecm.in" };
+
+/** Bulk OUT profiler */
+static struct profiler ecm_out_profiler __profiler =
+ { .name = "ecm.out" };
+
+/******************************************************************************
+ *
+ * Ethernet functional descriptor
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Locate Ethernet functional descriptor
+ *
+ * @v config Configuration descriptor
+ * @v interface Interface descriptor
+ * @ret desc Descriptor, or NULL if not found
+ */
+struct ecm_ethernet_descriptor *
+ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
+ struct usb_interface_descriptor *interface ) {
+ struct ecm_ethernet_descriptor *desc;
+
+ for_each_interface_descriptor ( desc, config, interface ) {
+ if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) &&
+ ( desc->subtype == CDC_SUBTYPE_ETHERNET ) )
+ return desc;
+ }
+ return NULL;
+}
+
+/**
+ * Get hardware MAC address
+ *
+ * @v usb USB device
+ * @v desc Ethernet functional descriptor
+ * @v hw_addr Hardware address to fill in
+ * @ret rc Return status code
+ */
+int ecm_fetch_mac ( struct usb_device *usb,
+ struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) {
+ char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ];
+ int len;
+ int rc;
+
+ /* Fetch MAC address string */
+ len = usb_get_string_descriptor ( usb, desc->mac, 0, buf,
+ sizeof ( buf ) );
+ if ( len < 0 ) {
+ rc = len;
+ return rc;
+ }
+
+ /* Sanity check */
+ if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) )
+ return -EINVAL;
+
+ /* Decode MAC address */
+ len = base16_decode ( buf, hw_addr, ETH_ALEN );
+ if ( len < 0 ) {
+ rc = len;
+ return rc;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * CDC-ECM communications interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Complete interrupt transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void ecm_intr_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct ecm_device *ecm = container_of ( ep, struct ecm_device,
+ usbnet.intr );
+ struct net_device *netdev = ecm->netdev;
+ struct usb_setup_packet *message;
+ size_t len = iob_len ( iobuf );
+
+ /* Profile completions */
+ profile_start ( &ecm_intr_profiler );
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open )
+ goto ignore;
+
+ /* Drop packets with errors */
+ if ( rc != 0 ) {
+ DBGC ( ecm, "ECM %p interrupt failed: %s\n",
+ ecm, strerror ( rc ) );
+ DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) );
+ goto error;
+ }
+
+ /* Extract message header */
+ if ( len < sizeof ( *message ) ) {
+ DBGC ( ecm, "ECM %p underlength interrupt:\n", ecm );
+ DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto error;
+ }
+ message = iobuf->data;
+
+ /* Parse message header */
+ switch ( message->request ) {
+
+ case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) :
+ if ( message->value && ! netdev_link_ok ( netdev ) ) {
+ DBGC ( ecm, "ECM %p link up\n", ecm );
+ netdev_link_up ( netdev );
+ } else if ( netdev_link_ok ( netdev ) && ! message->value ) {
+ DBGC ( ecm, "ECM %p link down\n", ecm );
+ netdev_link_down ( netdev );
+ }
+ break;
+
+ case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) :
+ /* Ignore */
+ break;
+
+ default:
+ DBGC ( ecm, "ECM %p unrecognised interrupt:\n", ecm );
+ DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -ENOTSUP;
+ goto error;
+ }
+
+ /* Free I/O buffer */
+ free_iob ( iobuf );
+ profile_stop ( &ecm_intr_profiler );
+
+ return;
+
+ error:
+ netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
+ ignore:
+ free_iob ( iobuf );
+ return;
+}
+
+/** Interrupt endpoint operations */
+static struct usb_endpoint_driver_operations ecm_intr_operations = {
+ .complete = ecm_intr_complete,
+};
+
+/******************************************************************************
+ *
+ * CDC-ECM data interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Complete bulk IN transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void ecm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
+ int rc ) {
+ struct ecm_device *ecm = container_of ( ep, struct ecm_device,
+ usbnet.in );
+ struct net_device *netdev = ecm->netdev;
+
+ /* Profile receive completions */
+ profile_start ( &ecm_in_profiler );
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open )
+ goto ignore;
+
+ /* Record USB errors against the network device */
+ if ( rc != 0 ) {
+ DBGC ( ecm, "ECM %p bulk IN failed: %s\n",
+ ecm, strerror ( rc ) );
+ goto error;
+ }
+
+ /* Hand off to network stack */
+ netdev_rx ( netdev, iob_disown ( iobuf ) );
+
+ profile_stop ( &ecm_in_profiler );
+ return;
+
+ error:
+ netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
+ ignore:
+ free_iob ( iobuf );
+}
+
+/** Bulk IN endpoint operations */
+static struct usb_endpoint_driver_operations ecm_in_operations = {
+ .complete = ecm_in_complete,
+};
+
+/**
+ * Transmit packet
+ *
+ * @v ecm CDC-ECM device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int ecm_out_transmit ( struct ecm_device *ecm,
+ struct io_buffer *iobuf ) {
+ int rc;
+
+ /* Profile transmissions */
+ profile_start ( &ecm_out_profiler );
+
+ /* Enqueue I/O buffer */
+ if ( ( rc = usb_stream ( &ecm->usbnet.out, iobuf, 1 ) ) != 0 )
+ return rc;
+
+ profile_stop ( &ecm_out_profiler );
+ return 0;
+}
+
+/**
+ * Complete bulk OUT transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void ecm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
+ int rc ) {
+ struct ecm_device *ecm = container_of ( ep, struct ecm_device,
+ usbnet.out );
+ struct net_device *netdev = ecm->netdev;
+
+ /* Report TX completion */
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+}
+
+/** Bulk OUT endpoint operations */
+static struct usb_endpoint_driver_operations ecm_out_operations = {
+ .complete = ecm_out_complete,
+};
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int ecm_open ( struct net_device *netdev ) {
+ struct ecm_device *ecm = netdev->priv;
+ struct usb_device *usb = ecm->usb;
+ unsigned int filter;
+ int rc;
+
+ /* Open USB network device */
+ if ( ( rc = usbnet_open ( &ecm->usbnet ) ) != 0 ) {
+ DBGC ( ecm, "ECM %p could not open: %s\n",
+ ecm, strerror ( rc ) );
+ goto err_open;
+ }
+
+ /* Set packet filter */
+ filter = ( ECM_PACKET_TYPE_PROMISCUOUS |
+ ECM_PACKET_TYPE_ALL_MULTICAST |
+ ECM_PACKET_TYPE_DIRECTED |
+ ECM_PACKET_TYPE_BROADCAST );
+ if ( ( rc = usb_control ( usb, ECM_SET_ETHERNET_PACKET_FILTER,
+ filter, ecm->usbnet.comms, NULL, 0 ) ) != 0 ){
+ DBGC ( ecm, "ECM %p could not set packet filter: %s\n",
+ ecm, strerror ( rc ) );
+ goto err_set_filter;
+ }
+
+ return 0;
+
+ err_set_filter:
+ usbnet_close ( &ecm->usbnet );
+ err_open:
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void ecm_close ( struct net_device *netdev ) {
+ struct ecm_device *ecm = netdev->priv;
+
+ /* Close USB network device */
+ usbnet_close ( &ecm->usbnet );
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int ecm_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct ecm_device *ecm = netdev->priv;
+ int rc;
+
+ /* Transmit packet */
+ if ( ( rc = ecm_out_transmit ( ecm, iobuf ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v netdev Network device
+ */
+static void ecm_poll ( struct net_device *netdev ) {
+ struct ecm_device *ecm = netdev->priv;
+ int rc;
+
+ /* Poll USB bus */
+ usb_poll ( ecm->bus );
+
+ /* Refill endpoints */
+ if ( ( rc = usbnet_refill ( &ecm->usbnet ) ) != 0 )
+ netdev_rx_err ( netdev, NULL, rc );
+}
+
+/** CDC-ECM network device operations */
+static struct net_device_operations ecm_operations = {
+ .open = ecm_open,
+ .close = ecm_close,
+ .transmit = ecm_transmit,
+ .poll = ecm_poll,
+};
+
+/******************************************************************************
+ *
+ * USB interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe device
+ *
+ * @v func USB function
+ * @v config Configuration descriptor
+ * @ret rc Return status code
+ */
+static int ecm_probe ( struct usb_function *func,
+ struct usb_configuration_descriptor *config ) {
+ struct usb_device *usb = func->usb;
+ struct net_device *netdev;
+ struct ecm_device *ecm;
+ struct usb_interface_descriptor *comms;
+ struct ecm_ethernet_descriptor *ethernet;
+ int rc;
+
+ /* Allocate and initialise structure */
+ netdev = alloc_etherdev ( sizeof ( *ecm ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &ecm_operations );
+ netdev->dev = &func->dev;
+ ecm = netdev->priv;
+ memset ( ecm, 0, sizeof ( *ecm ) );
+ ecm->usb = usb;
+ ecm->bus = usb->port->hub->bus;
+ ecm->netdev = netdev;
+ usbnet_init ( &ecm->usbnet, func, &ecm_intr_operations,
+ &ecm_in_operations, &ecm_out_operations );
+ usb_refill_init ( &ecm->usbnet.intr, 0, ECM_INTR_MAX_FILL );
+ usb_refill_init ( &ecm->usbnet.in, ECM_IN_MTU, ECM_IN_MAX_FILL );
+ DBGC ( ecm, "ECM %p on %s\n", ecm, func->name );
+
+ /* Describe USB network device */
+ if ( ( rc = usbnet_describe ( &ecm->usbnet, config ) ) != 0 ) {
+ DBGC ( ecm, "ECM %p could not describe: %s\n",
+ ecm, strerror ( rc ) );
+ goto err_describe;
+ }
+
+ /* Locate Ethernet descriptor */
+ comms = usb_interface_descriptor ( config, ecm->usbnet.comms, 0 );
+ assert ( comms != NULL );
+ ethernet = ecm_ethernet_descriptor ( config, comms );
+ if ( ! ethernet ) {
+ DBGC ( ecm, "ECM %p has no Ethernet descriptor\n", ecm );
+ rc = -EINVAL;
+ goto err_ethernet;
+ }
+
+ /* Fetch MAC address */
+ if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
+ DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n",
+ ecm, strerror ( rc ) );
+ goto err_fetch_mac;
+ }
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+
+ usb_func_set_drvdata ( func, ecm );
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register:
+ err_fetch_mac:
+ err_ethernet:
+ err_describe:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove device
+ *
+ * @v func USB function
+ */
+static void ecm_remove ( struct usb_function *func ) {
+ struct ecm_device *ecm = usb_func_get_drvdata ( func );
+ struct net_device *netdev = ecm->netdev;
+
+ unregister_netdev ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** CDC-ECM device IDs */
+static struct usb_device_id ecm_ids[] = {
+ {
+ .name = "cdc-ecm",
+ .vendor = USB_ANY_ID,
+ .product = USB_ANY_ID,
+ .class = {
+ .class = USB_CLASS_CDC,
+ .subclass = USB_SUBCLASS_CDC_ECM,
+ .protocol = 0,
+ },
+ },
+};
+
+/** CDC-ECM driver */
+struct usb_driver ecm_driver __usb_driver = {
+ .ids = ecm_ids,
+ .id_count = ( sizeof ( ecm_ids ) / sizeof ( ecm_ids[0] ) ),
+ .probe = ecm_probe,
+ .remove = ecm_remove,
+};
diff --git a/qemu/roms/ipxe/src/drivers/net/ecm.h b/qemu/roms/ipxe/src/drivers/net/ecm.h
new file mode 100644
index 000000000..83d324bdc
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/ecm.h
@@ -0,0 +1,93 @@
+#ifndef _ECM_H
+#define _ECM_H
+
+/** @file
+ *
+ * CDC-ECM USB Ethernet driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/usb.h>
+#include <ipxe/usbnet.h>
+#include <ipxe/cdc.h>
+
+/** CDC-ECM subclass */
+#define USB_SUBCLASS_CDC_ECM 0x06
+
+/** Set Ethernet packet filter */
+#define ECM_SET_ETHERNET_PACKET_FILTER \
+ ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \
+ USB_REQUEST_TYPE ( 0x43 ) )
+
+/** Ethernet packet types */
+enum ecm_ethernet_packet_filter {
+ /** Promiscuous mode */
+ ECM_PACKET_TYPE_PROMISCUOUS = 0x0001,
+ /** All multicast packets */
+ ECM_PACKET_TYPE_ALL_MULTICAST = 0x0002,
+ /** Unicast packets */
+ ECM_PACKET_TYPE_DIRECTED = 0x0004,
+ /** Broadcast packets */
+ ECM_PACKET_TYPE_BROADCAST = 0x0008,
+ /** Specified multicast packets */
+ ECM_PACKET_TYPE_MULTICAST = 0x0010,
+};
+
+/** An Ethernet Functional Descriptor */
+struct ecm_ethernet_descriptor {
+ /** Descriptor header */
+ struct usb_descriptor_header header;
+ /** Descriptor subtype */
+ uint8_t subtype;
+ /** MAC address string */
+ uint8_t mac;
+ /** Ethernet statistics bitmap */
+ uint32_t statistics;
+ /** Maximum segment size */
+ uint16_t mtu;
+ /** Multicast filter configuration */
+ uint16_t mcast;
+ /** Number of wake-on-LAN filters */
+ uint8_t wol;
+} __attribute__ (( packed ));
+
+/** A CDC-ECM network device */
+struct ecm_device {
+ /** USB device */
+ struct usb_device *usb;
+ /** USB bus */
+ struct usb_bus *bus;
+ /** Network device */
+ struct net_device *netdev;
+ /** USB network device */
+ struct usbnet_device usbnet;
+};
+
+/** Interrupt maximum fill level
+ *
+ * This is a policy decision.
+ */
+#define ECM_INTR_MAX_FILL 2
+
+/** Bulk IN maximum fill level
+ *
+ * This is a policy decision.
+ */
+#define ECM_IN_MAX_FILL 8
+
+/** Bulk IN buffer size
+ *
+ * This is a policy decision.
+ */
+#define ECM_IN_MTU ( ETH_FRAME_LEN + 4 /* possible VLAN header */ )
+
+extern struct ecm_ethernet_descriptor *
+ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
+ struct usb_interface_descriptor *interface );
+extern int ecm_fetch_mac ( struct usb_device *usb,
+ struct ecm_ethernet_descriptor *desc,
+ uint8_t *hw_addr );
+
+#endif /* _ECM_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/eepro.c b/qemu/roms/ipxe/src/drivers/net/eepro.c
index 909482bcc..97b4c4061 100644
--- a/qemu/roms/ipxe/src/drivers/net/eepro.c
+++ b/qemu/roms/ipxe/src/drivers/net/eepro.c
@@ -27,8 +27,18 @@ has 34 pins, the top row of 2 are not used.
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
@@ -591,9 +601,9 @@ static int eepro_probe ( struct nic *nic, struct isa_device *isa ) {
l_eepro = 0;
name = "Intel 82595-based LAN card";
}
- station_addr.saddr[0] = swap16(station_addr.saddr[0]);
- station_addr.saddr[1] = swap16(station_addr.saddr[1]);
- station_addr.saddr[2] = swap16(station_addr.saddr[2]);
+ station_addr.saddr[0] = bswap_16(station_addr.saddr[0]);
+ station_addr.saddr[1] = bswap_16(station_addr.saddr[1]);
+ station_addr.saddr[2] = bswap_16(station_addr.saddr[2]);
for (i = 0; i < ETH_ALEN; i++) {
nic->node_addr[i] = station_addr.caddr[i];
}
diff --git a/qemu/roms/ipxe/src/drivers/net/eepro100.c b/qemu/roms/ipxe/src/drivers/net/eepro100.c
index ede0a1a4b..1046cda39 100644
--- a/qemu/roms/ipxe/src/drivers/net/eepro100.c
+++ b/qemu/roms/ipxe/src/drivers/net/eepro100.c
@@ -1136,7 +1136,6 @@ PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM", 0),
PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection", 0),
PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection", 0),
PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection", 0),
-PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller", 0),
PCI_ROM(0x8086, 0x1065, "82562-3", "Intel 82562 based Fast Ethernet Connection", 0),
PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server", 0),
PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server", 0),
diff --git a/qemu/roms/ipxe/src/drivers/net/efi/nii.c b/qemu/roms/ipxe/src/drivers/net/efi/nii.c
index d0d7da95a..b91848f5c 100644
--- a/qemu/roms/ipxe/src/drivers/net/efi/nii.c
+++ b/qemu/roms/ipxe/src/drivers/net/efi/nii.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <strings.h>
@@ -168,6 +172,9 @@ struct nii_nic {
/** Saved task priority level */
EFI_TPL saved_tpl;
+ /** Media status is supported */
+ int media;
+
/** Current transmit buffer */
struct io_buffer *txbuf;
/** Current receive buffer */
@@ -408,6 +415,13 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb,
cdb.IFnum = nii->nii->IfNum;
/* Issue command */
+ DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n",
+ nii->dev.name, cdb.OpCode, cdb.OpFlags, cdb.IFnum,
+ ( cpb ? " cpb" : "" ), ( db ? " db" : "" ) );
+ if ( cpb )
+ DBGC2_HD ( nii, cpb, cpb_len );
+ if ( db )
+ DBGC2_HD ( nii, db, db_len );
nii->issue ( ( intptr_t ) &cdb );
/* Check completion status */
@@ -552,6 +566,7 @@ static int nii_get_init_info ( struct nii_nic *nii,
nii->buffer_len = db.MemoryRequired;
nii->mtu = ( db.FrameDataLen + db.MediaHeaderLen );
netdev->max_pkt_len = nii->mtu;
+ nii->media = ( stat & PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED );
return 0;
}
@@ -560,10 +575,12 @@ static int nii_get_init_info ( struct nii_nic *nii,
* Initialise UNDI
*
* @v nii NII NIC
+ * @v flags Flags
* @ret rc Return status code
*/
-static int nii_initialise ( struct nii_nic *nii ) {
+static int nii_initialise_flags ( struct nii_nic *nii, unsigned int flags ) {
PXE_CPB_INITIALIZE cpb;
+ PXE_DB_INITIALIZE db;
unsigned int op;
int stat;
int rc;
@@ -580,10 +597,13 @@ static int nii_initialise ( struct nii_nic *nii ) {
cpb.MemoryAddr = ( ( intptr_t ) nii->buffer );
cpb.MemoryLength = nii->buffer_len;
+ /* Construct data block */
+ memset ( &db, 0, sizeof ( db ) );
+
/* Issue command */
- op = NII_OP ( PXE_OPCODE_INITIALIZE,
- PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE );
- if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_INITIALIZE, flags );
+ if ( ( stat = nii_issue_cpb_db ( nii, op, &cpb, sizeof ( cpb ),
+ &db, sizeof ( db ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not initialise: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -599,6 +619,36 @@ static int nii_initialise ( struct nii_nic *nii ) {
}
/**
+ * Initialise UNDI
+ *
+ * @v nii NII NIC
+ * @ret rc Return status code
+ */
+static int nii_initialise ( struct nii_nic *nii ) {
+ unsigned int flags;
+
+ /* Initialise UNDI */
+ flags = PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE;
+ return nii_initialise_flags ( nii, flags );
+}
+
+/**
+ * Initialise UNDI and detect cable
+ *
+ * @v nii NII NIC
+ * @ret rc Return status code
+ */
+static int nii_initialise_and_detect ( struct nii_nic *nii ) {
+ unsigned int flags;
+
+ /* Initialise UNDI and detect cable. This is required to work
+ * around bugs in some Emulex NII drivers.
+ */
+ flags = PXE_OPFLAGS_INITIALIZE_DETECT_CABLE;
+ return nii_initialise_flags ( nii, flags );
+}
+
+/**
* Shut down UNDI
*
* @v nii NII NIC
@@ -630,6 +680,7 @@ static void nii_shutdown ( struct nii_nic *nii ) {
static int nii_get_station_address ( struct nii_nic *nii,
struct net_device *netdev ) {
PXE_DB_STATION_ADDRESS db;
+ unsigned int op;
int stat;
int rc;
@@ -638,8 +689,9 @@ static int nii_get_station_address ( struct nii_nic *nii,
goto err_initialise;
/* Issue command */
- if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_STATION_ADDRESS, &db,
- sizeof ( db ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_STATION_ADDRESS,
+ PXE_OPFLAGS_STATION_ADDRESS_READ );
+ if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not get station address: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -669,18 +721,25 @@ static int nii_get_station_address ( struct nii_nic *nii,
*/
static int nii_set_station_address ( struct nii_nic *nii,
struct net_device *netdev ) {
+ uint32_t implementation = nii->undi->Implementation;
PXE_CPB_STATION_ADDRESS cpb;
+ unsigned int op;
int stat;
int rc;
+ /* Fail if setting station address is unsupported */
+ if ( ! ( implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE ) )
+ return -ENOTSUP;
+
/* Construct parameter block */
memset ( &cpb, 0, sizeof ( cpb ) );
memcpy ( cpb.StationAddr, netdev->ll_addr,
netdev->ll_protocol->ll_addr_len );
/* Issue command */
- if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_STATION_ADDRESS,
- &cpb, sizeof ( cpb ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_STATION_ADDRESS,
+ PXE_OPFLAGS_STATION_ADDRESS_WRITE );
+ if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not set station address: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -697,21 +756,28 @@ static int nii_set_station_address ( struct nii_nic *nii,
* @ret rc Return status code
*/
static int nii_set_rx_filters ( struct nii_nic *nii ) {
+ uint32_t implementation = nii->undi->Implementation;
+ unsigned int flags;
unsigned int op;
int stat;
int rc;
+ /* Construct receive filter set */
+ flags = ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE |
+ PXE_OPFLAGS_RECEIVE_FILTER_UNICAST );
+ if ( implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED )
+ flags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+ if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED )
+ flags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED )
+ flags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+
/* Issue command */
- op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS,
- ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE |
- PXE_OPFLAGS_RECEIVE_FILTER_UNICAST |
- PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST |
- PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS |
- PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST ) );
+ op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, flags );
if ( ( stat = nii_issue ( nii, op ) ) < 0 ) {
rc = -EIO_STAT ( stat );
- DBGC ( nii, "NII %s could not set receive filters: %s\n",
- nii->dev.name, strerror ( rc ) );
+ DBGC ( nii, "NII %s could not set receive filters %#04x: %s\n",
+ nii->dev.name, flags, strerror ( rc ) );
return rc;
}
@@ -729,6 +795,7 @@ static int nii_transmit ( struct net_device *netdev,
struct io_buffer *iobuf ) {
struct nii_nic *nii = netdev->priv;
PXE_CPB_TRANSMIT cpb;
+ unsigned int op;
int stat;
int rc;
@@ -745,8 +812,10 @@ static int nii_transmit ( struct net_device *netdev,
cpb.MediaheaderLen = netdev->ll_protocol->ll_header_len;
/* Transmit packet */
- if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_TRANSMIT, &cpb,
- sizeof ( cpb ) ) ) < 0 ) {
+ op = NII_OP ( PXE_OPCODE_TRANSMIT,
+ ( PXE_OPFLAGS_TRANSMIT_WHOLE |
+ PXE_OPFLAGS_TRANSMIT_DONT_BLOCK ) );
+ if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not transmit: %s\n",
nii->dev.name, strerror ( rc ) );
@@ -772,12 +841,7 @@ static void nii_poll_tx ( struct net_device *netdev, unsigned int stat ) {
return;
/* Sanity check */
- if ( ! nii->txbuf ) {
- DBGC ( nii, "NII %s reported spurious TX completion\n",
- nii->dev.name );
- netdev_tx_err ( netdev, NULL, -EPIPE );
- return;
- }
+ assert ( nii->txbuf != NULL );
/* Complete transmission */
iobuf = nii->txbuf;
@@ -869,11 +933,14 @@ static void nii_poll ( struct net_device *netdev ) {
int stat;
int rc;
+ /* Construct data block */
+ memset ( &db, 0, sizeof ( db ) );
+
/* Get status */
op = NII_OP ( PXE_OPCODE_GET_STATUS,
( PXE_OPFLAGS_GET_INTERRUPT_STATUS |
- PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS |
- PXE_OPFLAGS_GET_MEDIA_STATUS ) );
+ ( nii->txbuf ? PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS : 0)|
+ ( nii->media ? PXE_OPFLAGS_GET_MEDIA_STATUS : 0 ) ) );
if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not get status: %s\n",
@@ -882,13 +949,15 @@ static void nii_poll ( struct net_device *netdev ) {
}
/* Process any TX completions */
- nii_poll_tx ( netdev, stat );
+ if ( nii->txbuf )
+ nii_poll_tx ( netdev, stat );
/* Process any RX completions */
nii_poll_rx ( netdev );
/* Check for link state changes */
- nii_poll_link ( netdev, stat );
+ if ( nii->media )
+ nii_poll_link ( netdev, stat );
}
/**
@@ -901,8 +970,18 @@ static int nii_open ( struct net_device *netdev ) {
struct nii_nic *nii = netdev->priv;
int rc;
- /* Initialise NIC */
- if ( ( rc = nii_initialise ( nii ) ) != 0 )
+ /* Initialise NIC
+ *
+ * Some Emulex NII drivers have a bug which prevents packets
+ * from being sent or received unless we specifically ask it
+ * to detect cable presence during initialisation. Work
+ * around these buggy drivers by requesting cable detection at
+ * this point, even though we don't care about link state here
+ * (and would prefer to have the NIC initialise even if no
+ * cable is present, to match the behaviour of all other iPXE
+ * drivers).
+ */
+ if ( ( rc = nii_initialise_and_detect ( nii ) ) != 0 )
goto err_initialise;
/* Attempt to set station address */
@@ -1023,8 +1102,9 @@ int nii_start ( struct efi_device *efidev ) {
nii->issue = ( ( ( void * ) nii->undi ) +
nii->undi->EntryPoint );
}
- DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p\n", nii->dev.name,
- nii->nii->MajorVer, nii->nii->MinorVer, nii->undi, nii->issue );
+ DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p impl %#08x\n",
+ nii->dev.name, nii->nii->MajorVer, nii->nii->MinorVer,
+ nii->undi, nii->issue, nii->undi->Implementation );
/* Open PCI I/O protocols and locate BARs */
if ( ( rc = nii_pci_open ( nii ) ) != 0 )
@@ -1048,6 +1128,10 @@ int nii_start ( struct efi_device *efidev ) {
DBGC ( nii, "NII %s registered as %s for %p %s\n", nii->dev.name,
netdev->name, device, efi_handle_name ( device ) );
+ /* Set initial link state (if media detection is not supported) */
+ if ( ! nii->media )
+ netdev_link_up ( netdev );
+
return 0;
unregister_netdev ( netdev );
diff --git a/qemu/roms/ipxe/src/drivers/net/efi/nii.h b/qemu/roms/ipxe/src/drivers/net/efi/nii.h
index de0ac687b..c10be9db5 100644
--- a/qemu/roms/ipxe/src/drivers/net/efi/nii.h
+++ b/qemu/roms/ipxe/src/drivers/net/efi/nii.h
@@ -7,7 +7,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
struct efi_device;
diff --git a/qemu/roms/ipxe/src/drivers/net/efi/snp.c b/qemu/roms/ipxe/src/drivers/net/efi/snp.c
index 2b5fc8618..acfcfba9f 100644
--- a/qemu/roms/ipxe/src/drivers/net/efi/snp.c
+++ b/qemu/roms/ipxe/src/drivers/net/efi/snp.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/efi/efi.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/efi/snponly.c b/qemu/roms/ipxe/src/drivers/net/efi/snponly.c
index 99f264bca..73abfdbf4 100644
--- a/qemu/roms/ipxe/src/drivers/net/efi/snponly.c
+++ b/qemu/roms/ipxe/src/drivers/net/efi/snponly.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <errno.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/etherfabric.c b/qemu/roms/ipxe/src/drivers/net/etherfabric.c
index 5e0efb1e1..29d117443 100644
--- a/qemu/roms/ipxe/src/drivers/net/etherfabric.c
+++ b/qemu/roms/ipxe/src/drivers/net/etherfabric.c
@@ -3176,7 +3176,7 @@ falcon_probe_nic_variant ( struct efab_nic *efab, struct pci_device *pci )
uint8_t revision;
/* PCI revision */
- pci_read_config_byte ( pci, PCI_CLASS_REVISION, &revision );
+ pci_read_config_byte ( pci, PCI_REVISION, &revision );
efab->pci_revision = revision;
/* Asic vs FPGA */
diff --git a/qemu/roms/ipxe/src/drivers/net/forcedeth.c b/qemu/roms/ipxe/src/drivers/net/forcedeth.c
index d8ece9a7a..79938cbbb 100644
--- a/qemu/roms/ipxe/src/drivers/net/forcedeth.c
+++ b/qemu/roms/ipxe/src/drivers/net/forcedeth.c
@@ -1749,10 +1749,8 @@ forcedeth_map_regs ( struct forcedeth_private *priv )
for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) {
pci_read_config_dword ( priv->pci_dev, reg, &bar );
- if ( ( ( bar & PCI_BASE_ADDRESS_SPACE ) ==
- PCI_BASE_ADDRESS_SPACE_MEMORY ) &&
- ( pci_bar_size ( priv->pci_dev, reg ) >=
- register_size ) ) {
+ if ( ( ! ( bar & PCI_BASE_ADDRESS_SPACE_IO ) ) &&
+ ( pci_bar_size ( priv->pci_dev, reg ) >= register_size ) ){
addr = pci_bar_start ( priv->pci_dev, reg );
break;
}
diff --git a/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c b/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c
index aace5ad56..fc7021c38 100644
--- a/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c
+++ b/qemu/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c
@@ -461,7 +461,7 @@ static int __devinit igbvf_sw_init ( struct igbvf_adapter *adapter )
hw->vendor_id = pdev->vendor;
hw->device_id = pdev->device;
- pci_read_config_byte ( pdev, PCI_REVISION_ID, &hw->revision_id );
+ pci_read_config_byte ( pdev, PCI_REVISION, &hw->revision_id );
pci_read_config_word ( pdev, PCI_COMMAND, &hw->bus.pci_cmd_word );
diff --git a/qemu/roms/ipxe/src/drivers/net/intel.c b/qemu/roms/ipxe/src/drivers/net/intel.c
index a89f947b2..6309e9aa5 100644
--- a/qemu/roms/ipxe/src/drivers/net/intel.c
+++ b/qemu/roms/ipxe/src/drivers/net/intel.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
@@ -248,32 +252,6 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) {
/******************************************************************************
*
- * Diagnostics
- *
- ******************************************************************************
- */
-
-/**
- * Dump diagnostic information
- *
- * @v intel Intel device
- */
-static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) {
-
- DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) "
- "RX %04x(%02x)/%04x(%02x)\n", intel,
- ( intel->tx.cons & 0xffff ),
- readl ( intel->regs + intel->tx.reg + INTEL_xDH ),
- ( intel->tx.prod & 0xffff ),
- readl ( intel->regs + intel->tx.reg + INTEL_xDT ),
- ( intel->rx.cons & 0xffff ),
- readl ( intel->regs + intel->rx.reg + INTEL_xDH ),
- ( intel->rx.prod & 0xffff ),
- readl ( intel->regs + intel->rx.reg + INTEL_xDT ) );
-}
-
-/******************************************************************************
- *
* Device reset
*
******************************************************************************
@@ -371,6 +349,67 @@ static void intel_check_link ( struct net_device *netdev ) {
/******************************************************************************
*
+ * Descriptors
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Populate transmit descriptor
+ *
+ * @v tx Transmit descriptor
+ * @v addr Data buffer address
+ * @v len Length of data
+ */
+void intel_describe_tx ( struct intel_descriptor *tx, physaddr_t addr,
+ size_t len ) {
+
+ /* Populate transmit descriptor */
+ tx->address = cpu_to_le64 ( addr );
+ tx->length = cpu_to_le16 ( len );
+ tx->flags = 0;
+ tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS |
+ INTEL_DESC_CMD_EOP );
+ tx->status = 0;
+}
+
+/**
+ * Populate advanced transmit descriptor
+ *
+ * @v tx Transmit descriptor
+ * @v addr Data buffer address
+ * @v len Length of data
+ */
+void intel_describe_tx_adv ( struct intel_descriptor *tx, physaddr_t addr,
+ size_t len ) {
+
+ /* Populate advanced transmit descriptor */
+ tx->address = cpu_to_le64 ( addr );
+ tx->length = cpu_to_le16 ( len );
+ tx->flags = INTEL_DESC_FL_DTYP_DATA;
+ tx->command = ( INTEL_DESC_CMD_DEXT | INTEL_DESC_CMD_RS |
+ INTEL_DESC_CMD_IFCS | INTEL_DESC_CMD_EOP );
+ tx->status = cpu_to_le32 ( INTEL_DESC_STATUS_PAYLEN ( len ) );
+}
+
+/**
+ * Populate receive descriptor
+ *
+ * @v rx Receive descriptor
+ * @v addr Data buffer address
+ * @v len Length of data
+ */
+void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr,
+ size_t len __unused ) {
+
+ /* Populate transmit descriptor */
+ rx->address = cpu_to_le64 ( addr );
+ rx->length = 0;
+ rx->status = 0;
+}
+
+/******************************************************************************
+ *
* Network device interface
*
******************************************************************************
@@ -479,10 +518,7 @@ void intel_refill_rx ( struct intel_nic *intel ) {
/* Populate receive descriptor */
address = virt_to_bus ( iobuf->data );
- rx->address = cpu_to_le64 ( address );
- rx->length = 0;
- rx->status = 0;
- rx->errors = 0;
+ intel->rx.describe ( rx, address, 0 );
/* Record I/O buffer */
assert ( intel->rx_iobuf[rx_idx] == NULL );
@@ -568,6 +604,13 @@ static int intel_open ( struct net_device *netdev ) {
/* Update link state */
intel_check_link ( netdev );
+ /* Apply required errata */
+ if ( intel->flags & INTEL_VMWARE ) {
+ DBGC ( intel, "INTEL %p applying VMware errata workaround\n",
+ intel );
+ intel->force_icr = INTEL_IRQ_RXT0;
+ }
+
return 0;
intel_destroy_ring ( intel, &intel->rx );
@@ -617,6 +660,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
unsigned int tx_idx;
unsigned int tx_tail;
physaddr_t address;
+ size_t len;
/* Get next transmit descriptor */
if ( ( intel->tx.prod - intel->tx.cons ) >= INTEL_TX_FILL ) {
@@ -629,11 +673,8 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
/* Populate transmit descriptor */
address = virt_to_bus ( iobuf->data );
- tx->address = cpu_to_le64 ( address );
- tx->length = cpu_to_le16 ( iob_len ( iobuf ) );
- tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS |
- INTEL_DESC_CMD_EOP );
- tx->status = 0;
+ len = iob_len ( iobuf );
+ intel->tx.describe ( tx, address, len );
wmb();
/* Notify card that there are packets ready to transmit */
@@ -644,7 +685,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
DBGC2 ( intel, "INTEL %p TX %d is [%llx,%llx)\n", intel, tx_idx,
( ( unsigned long long ) address ),
- ( ( unsigned long long ) address + iob_len ( iobuf ) ) );
+ ( ( unsigned long long ) address + len ) );
return 0;
}
@@ -667,7 +708,7 @@ void intel_poll_tx ( struct net_device *netdev ) {
tx = &intel->tx.desc[tx_idx];
/* Stop if descriptor is still in use */
- if ( ! ( tx->status & INTEL_DESC_STATUS_DD ) )
+ if ( ! ( tx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) )
return;
DBGC2 ( intel, "INTEL %p TX %d complete\n", intel, tx_idx );
@@ -698,7 +739,7 @@ void intel_poll_rx ( struct net_device *netdev ) {
rx = &intel->rx.desc[rx_idx];
/* Stop if descriptor is still in use */
- if ( ! ( rx->status & INTEL_DESC_STATUS_DD ) )
+ if ( ! ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) )
return;
/* Populate I/O buffer */
@@ -708,10 +749,10 @@ void intel_poll_rx ( struct net_device *netdev ) {
iob_put ( iobuf, len );
/* Hand off to network stack */
- if ( rx->errors ) {
+ if ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_RXE ) ) {
DBGC ( intel, "INTEL %p RX %d error (length %zd, "
- "errors %02x)\n",
- intel, rx_idx, len, rx->errors );
+ "status %08x)\n", intel, rx_idx, len,
+ le32_to_cpu ( rx->status ) );
netdev_rx_err ( netdev, iobuf, -EIO );
} else {
DBGC2 ( intel, "INTEL %p RX %d complete (length %zd)\n",
@@ -736,6 +777,7 @@ static void intel_poll ( struct net_device *netdev ) {
icr = readl ( intel->regs + INTEL_ICR );
profile_stop ( &intel_vm_poll_profiler );
profile_exclude ( &intel_vm_poll_profiler );
+ icr |= intel->force_icr;
if ( ! icr )
return;
@@ -755,6 +797,14 @@ static void intel_poll ( struct net_device *netdev ) {
if ( icr & INTEL_IRQ_LSC )
intel_check_link ( netdev );
+ /* Check for unexpected interrupts */
+ if ( icr & ~( INTEL_IRQ_TXDW | INTEL_IRQ_TXQE | INTEL_IRQ_LSC |
+ INTEL_IRQ_RXDMT0 | INTEL_IRQ_RXT0 | INTEL_IRQ_RXO ) ) {
+ DBGC ( intel, "INTEL %p unexpected ICR %08x\n", intel, icr );
+ /* Report as a TX error */
+ netdev_tx_err ( netdev, NULL, -ENOTSUP );
+ }
+
/* Refill RX ring */
intel_refill_rx ( intel );
}
@@ -817,8 +867,10 @@ static int intel_probe ( struct pci_device *pci ) {
memset ( intel, 0, sizeof ( *intel ) );
intel->port = PCI_FUNC ( pci->busdevfn );
intel->flags = pci->id->driver_data;
- intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD );
- intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD );
+ intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD,
+ intel_describe_tx );
+ intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD,
+ intel_describe_rx );
/* Fix up PCI device */
adjust_pci_device ( pci );
@@ -895,7 +947,7 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x100c, "82544gc", "82544GC (Copper)", 0 ),
PCI_ROM ( 0x8086, 0x100d, "82544gc-l", "82544GC (LOM)", 0 ),
PCI_ROM ( 0x8086, 0x100e, "82540em", "82540EM", 0 ),
- PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", 0 ),
+ PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", INTEL_VMWARE ),
PCI_ROM ( 0x8086, 0x1010, "82546eb", "82546EB (Copper)", 0 ),
PCI_ROM ( 0x8086, 0x1011, "82545em-f", "82545EM (Fiber)", 0 ),
PCI_ROM ( 0x8086, 0x1012, "82546eb-f", "82546EB (Fiber)", 0 ),
@@ -998,6 +1050,12 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ),
PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", 0 ),
PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ),
+ PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0),
+ PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0),
+ PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", 0 ),
+ PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ),
+ PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ),
+ PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", 0 ),
PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ),
PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ),
};
diff --git a/qemu/roms/ipxe/src/drivers/net/intel.h b/qemu/roms/ipxe/src/drivers/net/intel.h
index 8c4479bb4..ce9e3f467 100644
--- a/qemu/roms/ipxe/src/drivers/net/intel.h
+++ b/qemu/roms/ipxe/src/drivers/net/intel.h
@@ -7,7 +7,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/if_ether.h>
@@ -22,33 +22,38 @@ struct intel_descriptor {
uint64_t address;
/** Length */
uint16_t length;
- /** Reserved */
- uint8_t reserved_a;
+ /** Flags */
+ uint8_t flags;
/** Command */
uint8_t command;
/** Status */
- uint8_t status;
- /** Errors */
- uint8_t errors;
- /** Reserved */
- uint16_t reserved_b;
+ uint32_t status;
} __attribute__ (( packed ));
-/** Packet descriptor command bits */
-enum intel_descriptor_command {
- /** Report status */
- INTEL_DESC_CMD_RS = 0x08,
- /** Insert frame checksum (CRC) */
- INTEL_DESC_CMD_IFCS = 0x02,
- /** End of packet */
- INTEL_DESC_CMD_EOP = 0x01,
-};
+/** Descriptor type */
+#define INTEL_DESC_FL_DTYP( dtyp ) ( (dtyp) << 4 )
+#define INTEL_DESC_FL_DTYP_DATA INTEL_DESC_FL_DTYP ( 0x03 )
-/** Packet descriptor status bits */
-enum intel_descriptor_status {
- /** Descriptor done */
- INTEL_DESC_STATUS_DD = 0x01,
-};
+/** Descriptor extension */
+#define INTEL_DESC_CMD_DEXT 0x20
+
+/** Report status */
+#define INTEL_DESC_CMD_RS 0x08
+
+/** Insert frame checksum (CRC) */
+#define INTEL_DESC_CMD_IFCS 0x02
+
+/** End of packet */
+#define INTEL_DESC_CMD_EOP 0x01
+
+/** Descriptor done */
+#define INTEL_DESC_STATUS_DD 0x00000001UL
+
+/** Receive error */
+#define INTEL_DESC_STATUS_RXE 0x00000100UL
+
+/** Payload length */
+#define INTEL_DESC_STATUS_PAYLEN( len ) ( (len) << 14 )
/** Device Control Register */
#define INTEL_CTRL 0x00000UL
@@ -91,7 +96,9 @@ enum intel_descriptor_status {
/** Interrupt Cause Read Register */
#define INTEL_ICR 0x000c0UL
#define INTEL_IRQ_TXDW 0x00000001UL /**< Transmit descriptor done */
+#define INTEL_IRQ_TXQE 0x00000002UL /**< Transmit queue empty */
#define INTEL_IRQ_LSC 0x00000004UL /**< Link status change */
+#define INTEL_IRQ_RXDMT0 0x00000010UL /**< Receive queue low */
#define INTEL_IRQ_RXT0 0x00000080UL /**< Receive timer */
#define INTEL_IRQ_RXO 0x00000400UL /**< Receive overrun */
@@ -207,6 +214,15 @@ struct intel_ring {
unsigned int reg;
/** Length (in bytes) */
size_t len;
+
+ /** Populate descriptor
+ *
+ * @v desc Descriptor
+ * @v addr Data buffer address
+ * @v len Length of data
+ */
+ void ( * describe ) ( struct intel_descriptor *desc, physaddr_t addr,
+ size_t len );
};
/**
@@ -215,12 +231,39 @@ struct intel_ring {
* @v ring Descriptor ring
* @v count Number of descriptors
* @v reg Descriptor register block
+ * @v describe Method to populate descriptor
*/
static inline __attribute__ (( always_inline)) void
-intel_init_ring ( struct intel_ring *ring, unsigned int count,
- unsigned int reg ) {
+intel_init_ring ( struct intel_ring *ring, unsigned int count, unsigned int reg,
+ void ( * describe ) ( struct intel_descriptor *desc,
+ physaddr_t addr, size_t len ) ) {
+
ring->len = ( count * sizeof ( ring->desc[0] ) );
ring->reg = reg;
+ ring->describe = describe;
+}
+
+/** An Intel virtual function mailbox */
+struct intel_mailbox {
+ /** Mailbox control register */
+ unsigned int ctrl;
+ /** Mailbox memory base */
+ unsigned int mem;
+};
+
+/**
+ * Initialise mailbox
+ *
+ * @v mbox Mailbox
+ * @v ctrl Mailbox control register
+ * @v mem Mailbox memory register base
+ */
+static inline __attribute__ (( always_inline )) void
+intel_init_mbox ( struct intel_mailbox *mbox, unsigned int ctrl,
+ unsigned int mem ) {
+
+ mbox->ctrl = ctrl;
+ mbox->mem = mem;
}
/** An Intel network card */
@@ -231,6 +274,8 @@ struct intel_nic {
unsigned int port;
/** Flags */
unsigned int flags;
+ /** Forced interrupts */
+ unsigned int force_icr;
/** EEPROM */
struct nvs_device eeprom;
@@ -239,6 +284,9 @@ struct intel_nic {
/** EEPROM address shift */
unsigned int eerd_addr_shift;
+ /** Mailbox */
+ struct intel_mailbox mbox;
+
/** Transmit descriptor ring */
struct intel_ring tx;
/** Receive descriptor ring */
@@ -251,8 +299,35 @@ struct intel_nic {
enum intel_flags {
/** PBS/PBA errata workaround required */
INTEL_PBS_ERRATA = 0x0001,
+ /** VMware missing interrupt workaround required */
+ INTEL_VMWARE = 0x0002,
};
+/**
+ * Dump diagnostic information
+ *
+ * @v intel Intel device
+ */
+static inline void intel_diag ( struct intel_nic *intel ) {
+
+ DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) "
+ "RX %04x(%02x)/%04x(%02x)\n", intel,
+ ( intel->tx.cons & 0xffff ),
+ readl ( intel->regs + intel->tx.reg + INTEL_xDH ),
+ ( intel->tx.prod & 0xffff ),
+ readl ( intel->regs + intel->tx.reg + INTEL_xDT ),
+ ( intel->rx.cons & 0xffff ),
+ readl ( intel->regs + intel->rx.reg + INTEL_xDH ),
+ ( intel->rx.prod & 0xffff ),
+ readl ( intel->regs + intel->rx.reg + INTEL_xDT ) );
+}
+
+extern void intel_describe_tx ( struct intel_descriptor *tx,
+ physaddr_t addr, size_t len );
+extern void intel_describe_tx_adv ( struct intel_descriptor *tx,
+ physaddr_t addr, size_t len );
+extern void intel_describe_rx ( struct intel_descriptor *rx,
+ physaddr_t addr, size_t len );
extern int intel_create_ring ( struct intel_nic *intel,
struct intel_ring *ring );
extern void intel_destroy_ring ( struct intel_nic *intel,
diff --git a/qemu/roms/ipxe/src/drivers/net/intelvf.c b/qemu/roms/ipxe/src/drivers/net/intelvf.c
new file mode 100644
index 000000000..ac6fea745
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/intelvf.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ipxe/io.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include "intelvf.h"
+
+/** @file
+ *
+ * Intel 10/100/1000 virtual function network card driver
+ *
+ */
+
+/******************************************************************************
+ *
+ * Mailbox messages
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Write message to mailbox
+ *
+ * @v intel Intel device
+ * @v msg Message
+ */
+static void intelvf_mbox_write ( struct intel_nic *intel,
+ const union intelvf_msg *msg ) {
+ unsigned int i;
+
+ /* Write message */
+ DBGC2 ( intel, "INTEL %p sending message", intel );
+ for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){
+ DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] );
+ writel ( msg->dword[i], ( intel->regs + intel->mbox.mem +
+ ( i * sizeof ( msg->dword[0] ) ) ) );
+ }
+ DBGC2 ( intel, "\n" );
+}
+
+/**
+ * Read message from mailbox
+ *
+ * @v intel Intel device
+ * @v msg Message
+ */
+static void intelvf_mbox_read ( struct intel_nic *intel,
+ union intelvf_msg *msg ) {
+ unsigned int i;
+
+ /* Read message */
+ DBGC2 ( intel, "INTEL %p received message", intel );
+ for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){
+ msg->dword[i] = readl ( intel->regs + intel->mbox.mem +
+ ( i * sizeof ( msg->dword[0] ) ) );
+ DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] );
+ }
+ DBGC2 ( intel, "\n" );
+}
+
+/**
+ * Poll mailbox
+ *
+ * @v intel Intel device
+ * @ret rc Return status code
+ *
+ * Note that polling the mailbox may fail if the underlying PF is
+ * reset.
+ */
+int intelvf_mbox_poll ( struct intel_nic *intel ) {
+ struct intel_mailbox *mbox = &intel->mbox;
+ union intelvf_msg msg;
+ uint32_t ctrl;
+
+ /* Get mailbox status */
+ ctrl = readl ( intel->regs + mbox->ctrl );
+
+ /* Fail if a reset is in progress */
+ if ( ctrl & INTELVF_MBCTRL_RSTI )
+ return -EPIPE;
+
+ /* Acknowledge (and ignore) any received messages */
+ if ( ctrl & INTELVF_MBCTRL_PFSTS ) {
+ intelvf_mbox_read ( intel, &msg );
+ writel ( INTELVF_MBCTRL_ACK, intel->regs + mbox->ctrl );
+ }
+
+ return 0;
+}
+
+/**
+ * Wait for PF reset to complete
+ *
+ * @v intel Intel device
+ * @ret rc Return status code
+ */
+int intelvf_mbox_wait ( struct intel_nic *intel ) {
+ unsigned int i;
+ int rc;
+
+ /* Wait until a poll completes successfully */
+ for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) {
+
+ /* Check for successful poll */
+ if ( ( rc = intelvf_mbox_poll ( intel ) ) == 0 )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intel, "INTEL %p timed out waiting for reset\n", intel );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Send/receive mailbox message
+ *
+ * @v intel Intel device
+ * @v msg Message buffer
+ * @ret rc Return status code
+ */
+int intelvf_mbox_msg ( struct intel_nic *intel, union intelvf_msg *msg ) {
+ struct intel_mailbox *mbox = &intel->mbox;
+ uint32_t ctrl;
+ uint32_t seen = 0;
+ unsigned int i;
+
+ /* Sanity check */
+ assert ( ! ( msg->hdr & INTELVF_MSG_RESPONSE ) );
+
+ /* Handle mailbox */
+ for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) {
+
+ /* Attempt to claim mailbox, if we have not yet sent
+ * our message.
+ */
+ if ( ! ( seen & INTELVF_MBCTRL_VFU ) )
+ writel ( INTELVF_MBCTRL_VFU, intel->regs + mbox->ctrl );
+
+ /* Get mailbox status and record observed flags */
+ ctrl = readl ( intel->regs + mbox->ctrl );
+ seen |= ctrl;
+
+ /* If a reset is in progress, clear VFU and abort */
+ if ( ctrl & INTELVF_MBCTRL_RSTI ) {
+ writel ( 0, intel->regs + mbox->ctrl );
+ return -EPIPE;
+ }
+
+ /* Write message to mailbox, if applicable. This
+ * potentially overwrites a message sent by the PF (if
+ * the PF has simultaneously released PFU (thus
+ * allowing our VFU) and asserted PFSTS), but that
+ * doesn't really matter since there are no
+ * unsolicited PF->VF messages that require the actual
+ * message content to be observed.
+ */
+ if ( ctrl & INTELVF_MBCTRL_VFU )
+ intelvf_mbox_write ( intel, msg );
+
+ /* Read message from mailbox, if applicable. */
+ if ( ( seen & INTELVF_MBCTRL_VFU ) &&
+ ( seen & INTELVF_MBCTRL_PFACK ) &&
+ ( ctrl & INTELVF_MBCTRL_PFSTS ) )
+ intelvf_mbox_read ( intel, msg );
+
+ /* Acknowledge received message (if applicable),
+ * release VFU lock, and send message (if applicable).
+ */
+ ctrl = ( ( ( ctrl & INTELVF_MBCTRL_PFSTS ) ?
+ INTELVF_MBCTRL_ACK : 0 ) |
+ ( ( ctrl & INTELVF_MBCTRL_VFU ) ?
+ INTELVF_MBCTRL_REQ : 0 ) );
+ writel ( ctrl, intel->regs + mbox->ctrl );
+
+ /* Exit successfully if we have received a response */
+ if ( msg->hdr & INTELVF_MSG_RESPONSE ) {
+
+ /* Sanity check */
+ assert ( seen & INTELVF_MBCTRL_VFU );
+ assert ( seen & INTELVF_MBCTRL_PFACK );
+ assert ( seen & INTELVF_MBCTRL_PFSTS );
+
+ return 0;
+ }
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intel, "INTEL %p timed out waiting for mailbox (seen %08x)\n",
+ intel, seen );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Send reset message and get initial MAC address
+ *
+ * @v intel Intel device
+ * @v hw_addr Hardware address to fill in, or NULL
+ * @ret rc Return status code
+ */
+int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr ) {
+ union intelvf_msg msg;
+ int rc;
+
+ /* Send reset message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.hdr = INTELVF_MSG_TYPE_RESET;
+ if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p reset failed: %s\n",
+ intel, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check response */
+ if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_RESET ) {
+ DBGC ( intel, "INTEL %p reset unexpected response:\n", intel );
+ DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+ return -EPROTO;
+ }
+
+ /* Fill in MAC address, if applicable */
+ if ( hw_addr ) {
+ if ( msg.hdr & INTELVF_MSG_ACK ) {
+ memcpy ( hw_addr, msg.mac.mac, sizeof ( msg.mac.mac ) );
+ DBGC ( intel, "INTEL %p reset assigned MAC address "
+ "%s\n", intel, eth_ntoa ( hw_addr ) );
+ } else {
+ eth_random_addr ( hw_addr );
+ DBGC ( intel, "INTEL %p reset generated MAC address "
+ "%s\n", intel, eth_ntoa ( hw_addr ) );
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Send set MAC address message
+ *
+ * @v intel Intel device
+ * @v ll_addr Link-layer address
+ * @ret rc Return status code
+ */
+int intelvf_mbox_set_mac ( struct intel_nic *intel, const uint8_t *ll_addr ) {
+ union intelvf_msg msg;
+ int rc;
+
+ /* Send set MAC address message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.hdr = INTELVF_MSG_TYPE_SET_MAC;
+ memcpy ( msg.mac.mac, ll_addr, sizeof ( msg.mac.mac ) );
+ if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p set MAC address failed: %s\n",
+ intel, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check response */
+ if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MAC ) {
+ DBGC ( intel, "INTEL %p set MAC address unexpected response:\n",
+ intel );
+ DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+ return -EPROTO;
+ }
+
+ /* Check that we were allowed to set the MAC address */
+ if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) {
+ DBGC ( intel, "INTEL %p set MAC address refused\n", intel );
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/**
+ * Send set MTU message
+ *
+ * @v intel Intel device
+ * @v mtu Maximum packet size
+ * @ret rc Return status code
+ */
+int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ) {
+ union intelvf_msg msg;
+ int rc;
+
+ /* Send set MTU message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.hdr = INTELVF_MSG_TYPE_SET_MTU;
+ msg.mtu.mtu = mtu;
+ if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p set MTU failed: %s\n",
+ intel, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check response */
+ if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MTU ) {
+ DBGC ( intel, "INTEL %p set MTU unexpected response:\n",
+ intel );
+ DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+ return -EPROTO;
+ }
+
+ /* Check that we were allowed to set the MTU */
+ if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) {
+ DBGC ( intel, "INTEL %p set MTU refused\n", intel );
+ return -EPERM;
+ }
+
+ return 0;
+}
diff --git a/qemu/roms/ipxe/src/drivers/net/intelvf.h b/qemu/roms/ipxe/src/drivers/net/intelvf.h
new file mode 100644
index 000000000..d2f98d874
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/intelvf.h
@@ -0,0 +1,109 @@
+#ifndef _INTELVF_H
+#define _INTELVF_H
+
+/** @file
+ *
+ * Intel 10/100/1000 virtual function network card driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include "intel.h"
+
+/** Intel VF BAR size */
+#define INTELVF_BAR_SIZE ( 16 * 1024 )
+
+/** Mailbox Control Register */
+#define INTELVF_MBCTRL 0x0c40UL
+#define INTELVF_MBCTRL_REQ 0x00000001UL /**< Request for PF ready */
+#define INTELVF_MBCTRL_ACK 0x00000002UL /**< PF message received */
+#define INTELVF_MBCTRL_VFU 0x00000004UL /**< Buffer taken by VF */
+#define INTELVF_MBCTRL_PFU 0x00000008UL /**< Buffer taken to PF */
+#define INTELVF_MBCTRL_PFSTS 0x00000010UL /**< PF wrote a message */
+#define INTELVF_MBCTRL_PFACK 0x00000020UL /**< PF acknowledged message */
+#define INTELVF_MBCTRL_RSTI 0x00000040UL /**< PF reset in progress */
+#define INTELVF_MBCTRL_RSTD 0x00000080UL /**< PF reset complete */
+
+/** Mailbox Memory Register Base */
+#define INTELVF_MBMEM 0x0800UL
+
+/** Reset mailbox message */
+#define INTELVF_MSG_TYPE_RESET 0x00000001UL
+
+/** Set MAC address mailbox message */
+#define INTELVF_MSG_TYPE_SET_MAC 0x00000002UL
+
+/** Set MTU mailbox message */
+#define INTELVF_MSG_TYPE_SET_MTU 0x00000005UL
+
+/** Control ("ping") mailbox message */
+#define INTELVF_MSG_TYPE_CONTROL 0x00000100UL
+
+/** Message type mask */
+#define INTELVF_MSG_TYPE_MASK 0x0000ffffUL
+
+/** Message NACK flag */
+#define INTELVF_MSG_NACK 0x40000000UL
+
+/** Message ACK flag */
+#define INTELVF_MSG_ACK 0x80000000UL
+
+/** Message is a response */
+#define INTELVF_MSG_RESPONSE ( INTELVF_MSG_ACK | INTELVF_MSG_NACK )
+
+/** MAC address mailbox message */
+struct intelvf_msg_mac {
+ /** Message header */
+ uint32_t hdr;
+ /** MAC address */
+ uint8_t mac[ETH_ALEN];
+ /** Alignment padding */
+ uint8_t reserved[ (-ETH_ALEN) & 0x3 ];
+} __attribute__ (( packed ));
+
+/** Version number mailbox message */
+struct intelvf_msg_version {
+ /** Message header */
+ uint32_t hdr;
+ /** API version */
+ uint32_t version;
+} __attribute__ (( packed ));
+
+/** MTU mailbox message */
+struct intelvf_msg_mtu {
+ /** Message header */
+ uint32_t hdr;
+ /** Maximum packet size */
+ uint32_t mtu;
+} __attribute__ (( packed ));
+
+/** Mailbox message */
+union intelvf_msg {
+ /** Message header */
+ uint32_t hdr;
+ /** MAC address message */
+ struct intelvf_msg_mac mac;
+ /** Version number message */
+ struct intelvf_msg_version version;
+ /** MTU message */
+ struct intelvf_msg_mtu mtu;
+ /** Raw dwords */
+ uint32_t dword[0];
+};
+
+/** Maximum time to wait for mailbox message
+ *
+ * This is a policy decision.
+ */
+#define INTELVF_MBOX_MAX_WAIT_MS 500
+
+extern int intelvf_mbox_msg ( struct intel_nic *intel, union intelvf_msg *msg );
+extern int intelvf_mbox_poll ( struct intel_nic *intel );
+extern int intelvf_mbox_wait ( struct intel_nic *intel );
+extern int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr );
+extern int intelvf_mbox_set_mac ( struct intel_nic *intel,
+ const uint8_t *ll_addr );
+extern int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu );
+
+#endif /* _INTELVF_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/intelx.c b/qemu/roms/ipxe/src/drivers/net/intelx.c
index d69900e41..982b74f12 100644
--- a/qemu/roms/ipxe/src/drivers/net/intelx.c
+++ b/qemu/roms/ipxe/src/drivers/net/intelx.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
@@ -392,8 +396,10 @@ static int intelx_probe ( struct pci_device *pci ) {
netdev->dev = &pci->dev;
memset ( intel, 0, sizeof ( *intel ) );
intel->port = PCI_FUNC ( pci->busdevfn );
- intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELX_TD );
- intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELX_RD );
+ intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELX_TD,
+ intel_describe_tx );
+ intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELX_RD,
+ intel_describe_rx );
/* Fix up PCI device */
adjust_pci_device ( pci );
@@ -458,10 +464,15 @@ static void intelx_remove ( struct pci_device *pci ) {
/** PCI device IDs */
static struct pci_device_id intelx_nics[] = {
- PCI_ROM ( 0x8086, 0x10fb, "82599", "82599", 0 ),
- PCI_ROM ( 0x8086, 0x1528, "x540at2", "X540-AT2", 0 ),
- PCI_ROM ( 0x8086, 0x154d, "x520", "X520", 0 ),
- PCI_ROM ( 0x8086, 0x1557, "82599", "82599", 0 ),
+ PCI_ROM ( 0x8086, 0x10f7, "82599-kx4", "82599 (KX/KX4)", 0 ),
+ PCI_ROM ( 0x8086, 0x10f8, "82599-combo-backplane", "82599 (combined backplane; KR/KX4/KX)", 0 ),
+ PCI_ROM ( 0x8086, 0x10f9, "82599-cx4", "82599 (CX4)", 0 ),
+ PCI_ROM ( 0x8086, 0x10fb, "82599-sfp", "82599 (SFI/SFP+)", 0 ),
+ PCI_ROM ( 0x8086, 0x10fc, "82599-xaui", "82599 (XAUI/BX4)", 0 ),
+ PCI_ROM ( 0x8086, 0x1528, "x540t", "X540-AT2/X540-BT2", 0 ),
+ PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ),
+ PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ),
+ PCI_ROM ( 0x8086, 0x1560, "x540t1", "X540-AT2/X540-BT2 (with single port NVM)", 0 ),
};
/** PCI driver */
diff --git a/qemu/roms/ipxe/src/drivers/net/intelx.h b/qemu/roms/ipxe/src/drivers/net/intelx.h
index 60bb294d5..6383dfcad 100644
--- a/qemu/roms/ipxe/src/drivers/net/intelx.h
+++ b/qemu/roms/ipxe/src/drivers/net/intelx.h
@@ -7,7 +7,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/if_ether.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/intelxvf.c b/qemu/roms/ipxe/src/drivers/net/intelxvf.c
new file mode 100644
index 000000000..05e34c127
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/intelxvf.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ipxe/io.h>
+#include <ipxe/pci.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include "intelxvf.h"
+
+/** @file
+ *
+ * Intel 10 Gigabit Ethernet virtual function network card driver
+ *
+ */
+
+/******************************************************************************
+ *
+ * Diagnostics
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Dump statistics
+ *
+ * @v intel Intel device
+ */
+static __attribute__ (( unused )) void
+intelxvf_stats ( struct intel_nic *intel ) {
+
+ DBGC ( intel, "INTEL %p TX %d (%#x%08x) RX %d (%#x%08x) multi %d\n",
+ intel, readl ( intel->regs + INTELXVF_GPTC ),
+ readl ( intel->regs + INTELXVF_GOTCH ),
+ readl ( intel->regs + INTELXVF_GOTCL ),
+ readl ( intel->regs + INTELXVF_GPRC ),
+ readl ( intel->regs + INTELXVF_GORCH ),
+ readl ( intel->regs + INTELXVF_GORCL ),
+ readl ( intel->regs + INTELXVF_MPRC ) );
+}
+
+/******************************************************************************
+ *
+ * Device reset
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Reset hardware
+ *
+ * @v intel Intel device
+ */
+static void intelxvf_reset ( struct intel_nic *intel ) {
+
+ /* Perform a function-level reset */
+ writel ( INTELXVF_CTRL_RST, intel->regs + INTELXVF_CTRL );
+}
+
+/******************************************************************************
+ *
+ * Link state
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Check link state
+ *
+ * @v netdev Network device
+ */
+static void intelxvf_check_link ( struct net_device *netdev ) {
+ struct intel_nic *intel = netdev->priv;
+ uint32_t links;
+
+ /* Read link status */
+ links = readl ( intel->regs + INTELXVF_LINKS );
+ DBGC ( intel, "INTEL %p link status is %08x\n", intel, links );
+
+ /* Update network device */
+ if ( links & INTELXVF_LINKS_UP ) {
+ netdev_link_up ( netdev );
+ } else {
+ netdev_link_down ( netdev );
+ }
+}
+
+/******************************************************************************
+ *
+ * Mailbox messages
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Send negotiate API version message
+ *
+ * @v intel Intel device
+ * @v version Requested version
+ * @ret rc Return status code
+ */
+static int intelxvf_mbox_version ( struct intel_nic *intel,
+ unsigned int version ) {
+ union intelvf_msg msg;
+ int rc;
+
+ /* Send set MTU message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.hdr = INTELXVF_MSG_TYPE_VERSION;
+ msg.version.version = version;
+ if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p negotiate API version failed: %s\n",
+ intel, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check response */
+ if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELXVF_MSG_TYPE_VERSION ){
+ DBGC ( intel, "INTEL %p negotiate API version unexpected "
+ "response:\n", intel );
+ DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+ return -EPROTO;
+ }
+
+ /* Check that this version is supported */
+ if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) {
+ DBGC ( intel, "INTEL %p negotiate API version failed\n",
+ intel );
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxvf_open ( struct net_device *netdev ) {
+ struct intel_nic *intel = netdev->priv;
+ uint32_t srrctl;
+ uint32_t dca_rxctrl;
+ int rc;
+
+ /* Reset the function */
+ intelxvf_reset ( intel );
+
+ /* Notify PF that reset is complete */
+ if ( ( rc = intelvf_mbox_reset ( intel, NULL ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p could not reset: %s\n",
+ intel, strerror ( rc ) );
+ goto err_mbox_reset;
+ }
+
+ /* Negotiate API version 1.1. If we do not negotiate at least
+ * this version, then the RX datapath will remain disabled if
+ * the PF has jumbo frames enabled.
+ *
+ * Ignore failures, since the host may not actually support
+ * v1.1.
+ */
+ intelxvf_mbox_version ( intel, INTELXVF_MSG_VERSION_1_1 );
+
+ /* Set MAC address */
+ if ( ( rc = intelvf_mbox_set_mac ( intel, netdev->ll_addr ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p could not set MAC address: %s\n",
+ intel, strerror ( rc ) );
+ goto err_mbox_set_mac;
+ }
+
+ /* Set MTU */
+ if ( ( rc = intelvf_mbox_set_mtu ( intel, netdev->max_pkt_len ) ) != 0){
+ DBGC ( intel, "INTEL %p could not set MTU %zd: %s\n",
+ intel, netdev->max_pkt_len, strerror ( rc ) );
+ goto err_mbox_set_mtu;
+ }
+
+ /* Create transmit descriptor ring */
+ if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 )
+ goto err_create_tx;
+
+ /* Create receive descriptor ring */
+ if ( ( rc = intel_create_ring ( intel, &intel->rx ) ) != 0 )
+ goto err_create_rx;
+
+ /* Allocate interrupt vectors */
+ writel ( ( INTELXVF_IVAR_RX0_DEFAULT | INTELXVF_IVAR_RX0_VALID |
+ INTELXVF_IVAR_TX0_DEFAULT | INTELXVF_IVAR_TX0_VALID ),
+ intel->regs + INTELXVF_IVAR );
+ writel ( ( INTELXVF_IVARM_MBOX_DEFAULT | INTELXVF_IVARM_MBOX_VALID ),
+ intel->regs + INTELXVF_IVARM );
+
+ /* Configure receive buffer sizes and set receive descriptor type */
+ srrctl = readl ( intel->regs + INTELXVF_SRRCTL );
+ srrctl &= ~( INTELXVF_SRRCTL_BSIZE_MASK |
+ INTELXVF_SRRCTL_DESCTYPE_MASK );
+ srrctl |= ( INTELXVF_SRRCTL_BSIZE_DEFAULT |
+ INTELXVF_SRRCTL_DESCTYPE_DEFAULT );
+ writel ( srrctl, intel->regs + INTELXVF_SRRCTL );
+
+ /* Clear "must-be-zero" bit for direct cache access (DCA). We
+ * leave DCA disabled anyway, but if we do not clear this bit
+ * then the received packets contain garbage data.
+ */
+ dca_rxctrl = readl ( intel->regs + INTELXVF_DCA_RXCTRL );
+ dca_rxctrl &= ~INTELXVF_DCA_RXCTRL_MUST_BE_ZERO;
+ writel ( dca_rxctrl, intel->regs + INTELXVF_DCA_RXCTRL );
+
+ /* Fill receive ring */
+ intel_refill_rx ( intel );
+
+ /* Update link state */
+ intelxvf_check_link ( netdev );
+
+ return 0;
+
+ intel_destroy_ring ( intel, &intel->rx );
+ err_create_rx:
+ intel_destroy_ring ( intel, &intel->tx );
+ err_create_tx:
+ err_mbox_set_mtu:
+ err_mbox_set_mac:
+ err_mbox_reset:
+ intelxvf_reset ( intel );
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void intelxvf_close ( struct net_device *netdev ) {
+ struct intel_nic *intel = netdev->priv;
+
+ /* Destroy receive descriptor ring */
+ intel_destroy_ring ( intel, &intel->rx );
+
+ /* Discard any unused receive buffers */
+ intel_empty_rx ( intel );
+
+ /* Destroy transmit descriptor ring */
+ intel_destroy_ring ( intel, &intel->tx );
+
+ /* Reset the function */
+ intelxvf_reset ( intel );
+}
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v netdev Network device
+ */
+static void intelxvf_poll ( struct net_device *netdev ) {
+ struct intel_nic *intel = netdev->priv;
+ uint32_t eicr;
+ int rc;
+
+ /* Check for and acknowledge interrupts */
+ eicr = readl ( intel->regs + INTELXVF_EICR );
+ if ( ! eicr )
+ return;
+
+ /* Poll for TX completions, if applicable */
+ if ( eicr & INTELXVF_EIRQ_TX0 )
+ intel_poll_tx ( netdev );
+
+ /* Poll for RX completions, if applicable */
+ if ( eicr & INTELXVF_EIRQ_RX0 )
+ intel_poll_rx ( netdev );
+
+ /* Poll for mailbox messages, if applicable */
+ if ( eicr & INTELXVF_EIRQ_MBOX ) {
+
+ /* Poll mailbox */
+ if ( ( rc = intelvf_mbox_poll ( intel ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p mailbox poll failed!\n",
+ intel );
+ netdev_rx_err ( netdev, NULL, rc );
+ }
+
+ /* Update link state */
+ intelxvf_check_link ( netdev );
+ }
+
+ /* Refill RX ring */
+ intel_refill_rx ( intel );
+}
+
+/**
+ * Enable or disable interrupts
+ *
+ * @v netdev Network device
+ * @v enable Interrupts should be enabled
+ */
+static void intelxvf_irq ( struct net_device *netdev, int enable ) {
+ struct intel_nic *intel = netdev->priv;
+ uint32_t mask;
+
+ mask = ( INTELXVF_EIRQ_MBOX | INTELXVF_EIRQ_TX0 | INTELXVF_EIRQ_RX0 );
+ if ( enable ) {
+ writel ( mask, intel->regs + INTELXVF_EIMS );
+ } else {
+ writel ( mask, intel->regs + INTELXVF_EIMC );
+ }
+}
+
+/** Network device operations */
+static struct net_device_operations intelxvf_operations = {
+ .open = intelxvf_open,
+ .close = intelxvf_close,
+ .transmit = intel_transmit,
+ .poll = intelxvf_poll,
+ .irq = intelxvf_irq,
+};
+
+/******************************************************************************
+ *
+ * PCI interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+static int intelxvf_probe ( struct pci_device *pci ) {
+ struct net_device *netdev;
+ struct intel_nic *intel;
+ int rc;
+
+ /* Allocate and initialise net device */
+ netdev = alloc_etherdev ( sizeof ( *intel ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &intelxvf_operations );
+ intel = netdev->priv;
+ pci_set_drvdata ( pci, netdev );
+ netdev->dev = &pci->dev;
+ memset ( intel, 0, sizeof ( *intel ) );
+ intel_init_mbox ( &intel->mbox, INTELXVF_MBCTRL, INTELXVF_MBMEM );
+ intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELXVF_TD,
+ intel_describe_tx_adv );
+ intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELXVF_RD,
+ intel_describe_rx );
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Map registers */
+ intel->regs = ioremap ( pci->membase, INTELVF_BAR_SIZE );
+ if ( ! intel->regs ) {
+ rc = -ENODEV;
+ goto err_ioremap;
+ }
+
+ /* Reset the function */
+ intelxvf_reset ( intel );
+
+ /* Send reset message and fetch MAC address */
+ if ( ( rc = intelvf_mbox_reset ( intel, netdev->hw_addr ) ) != 0 ) {
+ DBGC ( intel, "INTEL %p could not reset and fetch MAC: %s\n",
+ intel, strerror ( rc ) );
+ goto err_mbox_reset;
+ }
+
+ /* Reset the function (since we will not respond to Control
+ * ("ping") mailbox messages until the network device is opened.
+ */
+ intelxvf_reset ( intel );
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register_netdev;
+
+ /* Set initial link state */
+ intelxvf_check_link ( netdev );
+
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register_netdev:
+ err_mbox_reset:
+ intelxvf_reset ( intel );
+ iounmap ( intel->regs );
+ err_ioremap:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void intelxvf_remove ( struct pci_device *pci ) {
+ struct net_device *netdev = pci_get_drvdata ( pci );
+ struct intel_nic *intel = netdev->priv;
+
+ /* Unregister network device */
+ unregister_netdev ( netdev );
+
+ /* Reset the NIC */
+ intelxvf_reset ( intel );
+
+ /* Free network device */
+ iounmap ( intel->regs );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** PCI device IDs */
+static struct pci_device_id intelxvf_nics[] = {
+ PCI_ROM ( 0x8086, 0x10ed, "82599-vf", "82599 VF", 0 ),
+ PCI_ROM ( 0x8086, 0x1515, "x540-vf", "X540 VF", 0 ),
+ PCI_ROM ( 0x8086, 0x1565, "x550-vf", "X550 VF", 0 ),
+ PCI_ROM ( 0x8086, 0x15a8, "x552-vf", "X552 VF", 0 ),
+};
+
+/** PCI driver */
+struct pci_driver intelxvf_driver __pci_driver = {
+ .ids = intelxvf_nics,
+ .id_count = ( sizeof ( intelxvf_nics ) / sizeof ( intelxvf_nics[0] ) ),
+ .probe = intelxvf_probe,
+ .remove = intelxvf_remove,
+};
diff --git a/qemu/roms/ipxe/src/drivers/net/intelxvf.h b/qemu/roms/ipxe/src/drivers/net/intelxvf.h
new file mode 100644
index 000000000..ad046a65c
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/intelxvf.h
@@ -0,0 +1,104 @@
+#ifndef _INTELXVF_H
+#define _INTELXVF_H
+
+/** @file
+ *
+ * Intel 10 Gigabit Ethernet virtual function network card driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include "intelvf.h"
+
+/** Control Register */
+#define INTELXVF_CTRL 0x0000UL
+#define INTELXVF_CTRL_RST 0x04000000UL /**< Function-level reset */
+
+/** Link Status Register */
+#define INTELXVF_LINKS 0x0010UL
+#define INTELXVF_LINKS_UP 0x40000000UL /**< Link up */
+
+/** Extended Interrupt Cause Read Register */
+#define INTELXVF_EICR 0x0100UL
+#define INTELXVF_EIRQ_RX0 0x00000001UL /**< RX queue 0 (via IVAR) */
+#define INTELXVF_EIRQ_TX0 0x00000002UL /**< TX queue 0 (via IVAR) */
+#define INTELXVF_EIRQ_MBOX 0x00000004UL /**< Mailbox (via IVARM) */
+
+/** Extended Interrupt Mask Set/Read Register */
+#define INTELXVF_EIMS 0x0108UL
+
+/** Extended Interrupt Mask Clear Register */
+#define INTELXVF_EIMC 0x010cUL
+
+/** Interrupt Vector Allocation Register */
+#define INTELXVF_IVAR 0x0120UL
+#define INTELXVF_IVAR_RX0(bit) ( (bit) << 0 ) /**< RX queue 0 allocation */
+#define INTELXVF_IVAR_RX0_DEFAULT INTELXVF_IVAR_RX0 ( 0x00 )
+#define INTELXVF_IVAR_RX0_MASK INTELXVF_IVAR_RX0 ( 0x01 )
+#define INTELXVF_IVAR_RX0_VALID 0x00000080UL /**< RX queue 0 valid */
+#define INTELXVF_IVAR_TX0(bit) ( (bit) << 8 ) /**< TX queue 0 allocation */
+#define INTELXVF_IVAR_TX0_DEFAULT INTELXVF_IVAR_TX0 ( 0x01 )
+#define INTELXVF_IVAR_TX0_MASK INTELXVF_IVAR_TX0 ( 0x01 )
+#define INTELXVF_IVAR_TX0_VALID 0x00008000UL /**< TX queue 0 valid */
+
+/** Interrupt Vector Allocation Miscellaneous Register */
+#define INTELXVF_IVARM 0x0140UL
+#define INTELXVF_IVARM_MBOX(bit) ( (bit) << 0 ) /**< Mailbox allocation */
+#define INTELXVF_IVARM_MBOX_DEFAULT INTELXVF_IVARM_MBOX ( 0x02 )
+#define INTELXVF_IVARM_MBOX_MASK INTELXVF_IVARM_MBOX ( 0x03 )
+#define INTELXVF_IVARM_MBOX_VALID 0x00000080UL /**< Mailbox valid */
+
+/** Mailbox Memory Register Base */
+#define INTELXVF_MBMEM 0x0200UL
+
+/** Mailbox Control Register */
+#define INTELXVF_MBCTRL 0x02fcUL
+
+/** Receive Descriptor register block */
+#define INTELXVF_RD 0x1000UL
+
+/** RX DCA Control Register */
+#define INTELXVF_DCA_RXCTRL 0x100cUL
+#define INTELXVF_DCA_RXCTRL_MUST_BE_ZERO 0x00001000UL /**< Must be zero */
+
+/** Split Receive Control Register */
+#define INTELXVF_SRRCTL 0x1014UL
+#define INTELXVF_SRRCTL_BSIZE(kb) ( (kb) << 0 ) /**< Receive buffer size */
+#define INTELXVF_SRRCTL_BSIZE_DEFAULT INTELXVF_SRRCTL_BSIZE ( 0x02 )
+#define INTELXVF_SRRCTL_BSIZE_MASK INTELXVF_SRRCTL_BSIZE ( 0x1f )
+#define INTELXVF_SRRCTL_DESCTYPE(typ) ( (typ) << 25 ) /**< Descriptor type */
+#define INTELXVF_SRRCTL_DESCTYPE_DEFAULT INTELXVF_SRRCTL_DESCTYPE ( 0x00 )
+#define INTELXVF_SRRCTL_DESCTYPE_MASK INTELXVF_SRRCTL_DESCTYPE ( 0x07 )
+
+/** Good Packets Received Count */
+#define INTELXVF_GPRC 0x101c
+
+/** Good Packets Received Count Low */
+#define INTELXVF_GORCL 0x1020
+
+/** Good Packets Received Count High */
+#define INTELXVF_GORCH 0x1024
+
+/* Multicast Packets Received Count */
+#define INTELXVF_MPRC 0x1034
+
+/** Transmit Descriptor register block */
+#define INTELXVF_TD 0x2000UL
+
+/** Good Packets Transmitted Count */
+#define INTELXVF_GPTC 0x201c
+
+/** Good Packets Transmitted Count Low */
+#define INTELXVF_GOTCL 0x2020
+
+/** Good Packets Transmitted Count High */
+#define INTELXVF_GOTCH 0x2024
+
+/** Negotiate API version mailbox message */
+#define INTELXVF_MSG_TYPE_VERSION 0x00000008UL
+
+/** API version 1.1 */
+#define INTELXVF_MSG_VERSION_1_1 0x00000002UL
+
+#endif /* _INTELXVF_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/ipoib.c b/qemu/roms/ipxe/src/drivers/net/ipoib.c
index 1b5391776..6552d764e 100644
--- a/qemu/roms/ipxe/src/drivers/net/ipoib.c
+++ b/qemu/roms/ipxe/src/drivers/net/ipoib.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
@@ -29,8 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/errortab.h>
#include <ipxe/malloc.h>
#include <ipxe/if_arp.h>
+#include <ipxe/arp.h>
#include <ipxe/if_ether.h>
#include <ipxe/ethernet.h>
+#include <ipxe/ip.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/infiniband.h>
@@ -44,6 +50,20 @@ FILE_LICENCE ( GPL2_OR_LATER );
* IP over Infiniband
*/
+/* Disambiguate the various error causes */
+#define ENXIO_ARP_REPLY __einfo_error ( EINFO_ENXIO_ARP_REPLY )
+#define EINFO_ENXIO_ARP_REPLY \
+ __einfo_uniqify ( EINFO_ENXIO, 0x01, \
+ "Missing REMAC for ARP reply target address" )
+#define ENXIO_NON_IPV4 __einfo_error ( EINFO_ENXIO_NON_IPV4 )
+#define EINFO_ENXIO_NON_IPV4 \
+ __einfo_uniqify ( EINFO_ENXIO, 0x02, \
+ "Missing REMAC for non-IPv4 packet" )
+#define ENXIO_ARP_SENT __einfo_error ( EINFO_ENXIO_ARP_SENT )
+#define EINFO_ENXIO_ARP_SENT \
+ __einfo_uniqify ( EINFO_ENXIO, 0x03, \
+ "Missing REMAC for IPv4 packet (ARP sent)" )
+
/** Number of IPoIB send work queue entries */
#define IPOIB_NUM_SEND_WQES 2
@@ -96,6 +116,8 @@ struct errortab ipoib_errors[] __errortab = {
__einfo_errortab ( EINFO_EINPROGRESS_JOINING ),
};
+static struct net_device_operations ipoib_operations;
+
/****************************************************************************
*
* IPoIB REMAC cache
@@ -124,8 +146,10 @@ static struct ipoib_mac * ipoib_find_remac ( struct ipoib_device *ipoib,
const struct ipoib_remac *remac ) {
struct ipoib_peer *peer;
- /* Check for broadcast REMAC */
- if ( is_broadcast_ether_addr ( remac ) )
+ /* Check for broadcast or multicast REMAC. We transmit
+ * multicasts as broadcasts for simplicity.
+ */
+ if ( is_multicast_ether_addr ( remac ) )
return &ipoib->broadcast;
/* Try to find via REMAC cache */
@@ -202,14 +226,20 @@ static void ipoib_flush_remac ( struct ipoib_device *ipoib ) {
* @ret discarded Number of cached items discarded
*/
static unsigned int ipoib_discard_remac ( void ) {
- struct ib_device *ibdev;
+ struct net_device *netdev;
struct ipoib_device *ipoib;
struct ipoib_peer *peer;
unsigned int discarded = 0;
/* Try to discard one cache entry for each IPoIB device */
- for_each_ibdev ( ibdev ) {
- ipoib = ib_get_ownerdata ( ibdev );
+ for_each_netdev ( netdev ) {
+
+ /* Skip non-IPoIB devices */
+ if ( netdev->op != &ipoib_operations )
+ continue;
+ ipoib = netdev->priv;
+
+ /* Discard least recently used cache entry (if any) */
list_for_each_entry_reverse ( peer, &ipoib->peers, list ) {
list_del ( &peer->list );
free ( peer );
@@ -222,7 +252,7 @@ static unsigned int ipoib_discard_remac ( void ) {
}
/** IPoIB cache discarder */
-struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_NORMAL ) = {
+struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_EXPENSIVE ) = {
.discard = ipoib_discard_remac,
};
@@ -324,8 +354,11 @@ static int ipoib_translate_tx_arp ( struct net_device *netdev,
/* Look up REMAC, if applicable */
if ( arphdr->ar_op == ARPOP_REPLY ) {
target_ha = ipoib_find_remac ( ipoib, arp_target_pa ( arphdr ));
- if ( ! target_ha )
- return -ENXIO;
+ if ( ! target_ha ) {
+ DBGC ( ipoib, "IPoIB %p no REMAC for %s ARP reply\n",
+ ipoib, eth_ntoa ( arp_target_pa ( arphdr ) ) );
+ return -ENXIO_ARP_REPLY;
+ }
}
/* Construct new packet */
@@ -461,6 +494,7 @@ static int ipoib_transmit ( struct net_device *netdev,
struct ipoib_device *ipoib = netdev->priv;
struct ib_device *ibdev = ipoib->ibdev;
struct ethhdr *ethhdr;
+ struct iphdr *iphdr;
struct ipoib_hdr *ipoib_hdr;
struct ipoib_mac *mac;
struct ib_address_vector dest;
@@ -485,9 +519,34 @@ static int ipoib_transmit ( struct net_device *netdev,
iob_pull ( iobuf, sizeof ( *ethhdr ) );
/* Identify destination address */
- mac = ipoib_find_remac ( ipoib, ( ( void *) ethhdr->h_dest ) );
- if ( ! mac )
- return -ENXIO;
+ mac = ipoib_find_remac ( ipoib, ( ( void * ) ethhdr->h_dest ) );
+ if ( ! mac ) {
+ /* Generate a new ARP request (if possible) to trigger
+ * population of the REMAC cache entry.
+ */
+ if ( ( net_proto != htons ( ETH_P_IP ) ) ||
+ ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) ) {
+ DBGC ( ipoib, "IPoIB %p no REMAC for %s non-IPv4 "
+ "packet type %04x\n", ipoib,
+ eth_ntoa ( ethhdr->h_dest ),
+ ntohs ( net_proto ) );
+ return -ENXIO_NON_IPV4;
+ }
+ iphdr = iobuf->data;
+ if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol,
+ &iphdr->dest, &iphdr->src ) ) !=0){
+ DBGC ( ipoib, "IPoIB %p could not ARP for %s/%s/",
+ ipoib, eth_ntoa ( ethhdr->h_dest ),
+ inet_ntoa ( iphdr->dest ) );
+ DBGC ( ipoib, "%s: %s\n", inet_ntoa ( iphdr->src ),
+ strerror ( rc ) );
+ return rc;
+ }
+ DBGC ( ipoib, "IPoIB %p no REMAC for %s/%s/", ipoib,
+ eth_ntoa ( ethhdr->h_dest ), inet_ntoa ( iphdr->dest ) );
+ DBGC ( ipoib, "%s\n", inet_ntoa ( iphdr->src ) );
+ return -ENXIO_ARP_SENT;
+ }
/* Translate packet if applicable */
if ( ( rc = ipoib_translate_tx ( netdev, iobuf, net_proto ) ) != 0 )
@@ -732,7 +791,8 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) {
int rc;
/* Leave existing broadcast group */
- ipoib_leave_broadcast_group ( ipoib );
+ if ( ipoib->qp )
+ ipoib_leave_broadcast_group ( ipoib );
/* Update MAC address based on potentially-new GID prefix */
memcpy ( &ipoib->mac.gid.s.prefix, &ibdev->gid.s.prefix,
@@ -747,7 +807,7 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) {
netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) );
/* Join new broadcast group */
- if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) &&
+ if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && ipoib->qp &&
( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) ) {
DBGC ( ipoib, "IPoIB %p could not rejoin broadcast group: "
"%s\n", ipoib, strerror ( rc ) );
@@ -835,7 +895,9 @@ static void ipoib_close ( struct net_device *netdev ) {
/* Tear down the queues */
ib_destroy_qp ( ibdev, ipoib->qp );
+ ipoib->qp = NULL;
ib_destroy_cq ( ibdev, ipoib->cq );
+ ipoib->cq = NULL;
/* Close IB device */
ib_close ( ibdev );
diff --git a/qemu/roms/ipxe/src/drivers/net/legacy.c b/qemu/roms/ipxe/src/drivers/net/legacy.c
index 4edbef162..73a80194f 100644
--- a/qemu/roms/ipxe/src/drivers/net/legacy.c
+++ b/qemu/roms/ipxe/src/drivers/net/legacy.c
@@ -17,7 +17,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
struct nic nic;
diff --git a/qemu/roms/ipxe/src/drivers/net/mii.c b/qemu/roms/ipxe/src/drivers/net/mii.c
index c4d32514d..9b297029a 100644
--- a/qemu/roms/ipxe/src/drivers/net/mii.c
+++ b/qemu/roms/ipxe/src/drivers/net/mii.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <unistd.h>
@@ -111,3 +115,35 @@ int mii_reset ( struct mii_interface *mii ) {
DBGC ( mii, "MII %p timed out waiting for reset\n", mii );
return -ETIMEDOUT;
}
+
+/**
+ * Update link status via MII
+ *
+ * @v mii MII interface
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+int mii_check_link ( struct mii_interface *mii, struct net_device *netdev ) {
+ int bmsr;
+ int link;
+ int rc;
+
+ /* Read BMSR */
+ bmsr = mii_read ( mii, MII_BMSR );
+ if ( bmsr < 0 ) {
+ rc = bmsr;
+ return rc;
+ }
+
+ /* Report link status */
+ link = ( bmsr & BMSR_LSTATUS );
+ DBGC ( mii, "MII %p link %s (BMSR %#04x)\n",
+ mii, ( link ? "up" : "down" ), bmsr );
+ if ( link ) {
+ netdev_link_up ( netdev );
+ } else {
+ netdev_link_down ( netdev );
+ }
+
+ return 0;
+}
diff --git a/qemu/roms/ipxe/src/drivers/net/myson.c b/qemu/roms/ipxe/src/drivers/net/myson.c
index 6abb55660..84a550596 100644
--- a/qemu/roms/ipxe/src/drivers/net/myson.c
+++ b/qemu/roms/ipxe/src/drivers/net/myson.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/myson.h b/qemu/roms/ipxe/src/drivers/net/myson.h
index 8d7cc5855..05a6b8a58 100644
--- a/qemu/roms/ipxe/src/drivers/net/myson.h
+++ b/qemu/roms/ipxe/src/drivers/net/myson.h
@@ -7,7 +7,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/if_ether.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/ncm.c b/qemu/roms/ipxe/src/drivers/net/ncm.c
new file mode 100644
index 000000000..10728d2a1
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/ncm.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/profile.h>
+#include <ipxe/usb.h>
+#include <ipxe/usbnet.h>
+#include "ecm.h"
+#include "ncm.h"
+
+/** @file
+ *
+ * CDC-NCM USB Ethernet driver
+ *
+ */
+
+/** Interrupt completion profiler */
+static struct profiler ncm_intr_profiler __profiler =
+ { .name = "ncm.intr" };
+
+/** Bulk IN completion profiler */
+static struct profiler ncm_in_profiler __profiler =
+ { .name = "ncm.in" };
+
+/** Bulk IN per-datagram profiler */
+static struct profiler ncm_in_datagram_profiler __profiler =
+ { .name = "ncm.in_dgram" };
+
+/** Bulk OUT profiler */
+static struct profiler ncm_out_profiler __profiler =
+ { .name = "ncm.out" };
+
+/******************************************************************************
+ *
+ * CDC-NCM communications interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Complete interrupt transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void ncm_intr_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct ncm_device *ncm = container_of ( ep, struct ncm_device,
+ usbnet.intr );
+ struct net_device *netdev = ncm->netdev;
+ struct usb_setup_packet *message;
+ size_t len = iob_len ( iobuf );
+
+ /* Profile completions */
+ profile_start ( &ncm_intr_profiler );
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open )
+ goto ignore;
+
+ /* Ignore packets with errors */
+ if ( rc != 0 ) {
+ DBGC ( ncm, "NCM %p interrupt failed: %s\n",
+ ncm, strerror ( rc ) );
+ DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
+ goto error;
+ }
+
+ /* Extract message header */
+ if ( len < sizeof ( *message ) ) {
+ DBGC ( ncm, "NCM %p underlength interrupt:\n", ncm );
+ DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto error;
+ }
+ message = iobuf->data;
+
+ /* Parse message header */
+ switch ( message->request ) {
+
+ case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) :
+ if ( message->value ) {
+ DBGC ( ncm, "NCM %p link up\n", ncm );
+ netdev_link_up ( netdev );
+ } else {
+ DBGC ( ncm, "NCM %p link down\n", ncm );
+ netdev_link_down ( netdev );
+ }
+ break;
+
+ case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) :
+ /* Ignore */
+ break;
+
+ default:
+ DBGC ( ncm, "NCM %p unrecognised interrupt:\n", ncm );
+ DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
+ goto error;
+ }
+
+ /* Free I/O buffer */
+ free_iob ( iobuf );
+ profile_stop ( &ncm_intr_profiler );
+
+ return;
+
+ error:
+ netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
+ ignore:
+ free_iob ( iobuf );
+ return;
+}
+
+/** Interrupt endpoint operations */
+static struct usb_endpoint_driver_operations ncm_intr_operations = {
+ .complete = ncm_intr_complete,
+};
+
+/******************************************************************************
+ *
+ * CDC-NCM data interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Prefill bulk IN endpoint
+ *
+ * @v ncm CDC-NCM device
+ * @ret rc Return status code
+ */
+static int ncm_in_prefill ( struct ncm_device *ncm ) {
+ struct usb_bus *bus = ncm->bus;
+ size_t mtu;
+ unsigned int count;
+ int rc;
+
+ /* Some devices have a very small number of internal buffers,
+ * and rely on being able to pack multiple packets into each
+ * buffer. We therefore want to use large buffers if
+ * possible. However, large allocations have a reasonable
+ * chance of failure, especially if this is not the first or
+ * only device to be opened.
+ *
+ * We therefore attempt to find a usable buffer size, starting
+ * large and working downwards until allocation succeeds.
+ * Smaller buffers will still work, albeit with a higher
+ * chance of packet loss and so lower overall throughput.
+ */
+ for ( mtu = ncm->mtu ; mtu >= NCM_MIN_NTB_INPUT_SIZE ; mtu >>= 1 ) {
+
+ /* Attempt allocation at this MTU */
+ if ( mtu > NCM_MAX_NTB_INPUT_SIZE )
+ continue;
+ if ( mtu > bus->mtu )
+ continue;
+ count = ( NCM_IN_MIN_SIZE / mtu );
+ if ( count < NCM_IN_MIN_COUNT )
+ count = NCM_IN_MIN_COUNT;
+ if ( ( count * mtu ) > NCM_IN_MAX_SIZE )
+ continue;
+ usb_refill_init ( &ncm->usbnet.in, mtu, count );
+ if ( ( rc = usb_prefill ( &ncm->usbnet.in ) ) != 0 ) {
+ DBGC ( ncm, "NCM %p could not prefill %dx %zd-byte "
+ "buffers for bulk IN\n", ncm, count, mtu );
+ continue;
+ }
+
+ DBGC ( ncm, "NCM %p using %dx %zd-byte buffers for bulk IN\n",
+ ncm, count, mtu );
+ return 0;
+ }
+
+ DBGC ( ncm, "NCM %p could not prefill bulk IN endpoint\n", ncm );
+ return -ENOMEM;
+}
+
+/**
+ * Complete bulk IN transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void ncm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
+ int rc ) {
+ struct ncm_device *ncm = container_of ( ep, struct ncm_device,
+ usbnet.in );
+ struct net_device *netdev = ncm->netdev;
+ struct ncm_transfer_header *nth;
+ struct ncm_datagram_pointer *ndp;
+ struct ncm_datagram_descriptor *desc;
+ struct io_buffer *pkt;
+ unsigned int remaining;
+ size_t ndp_offset;
+ size_t ndp_len;
+ size_t pkt_offset;
+ size_t pkt_len;
+ size_t headroom;
+ size_t len;
+
+ /* Profile overall bulk IN completion */
+ profile_start ( &ncm_in_profiler );
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open )
+ goto ignore;
+
+ /* Record USB errors against the network device */
+ if ( rc != 0 ) {
+ DBGC ( ncm, "NCM %p bulk IN failed: %s\n",
+ ncm, strerror ( rc ) );
+ goto error;
+ }
+
+ /* Locate transfer header */
+ len = iob_len ( iobuf );
+ if ( sizeof ( *nth ) > len ) {
+ DBGC ( ncm, "NCM %p packet too short for NTH:\n", ncm );
+ rc = -EINVAL;
+ goto error;
+ }
+ nth = iobuf->data;
+
+ /* Locate datagram pointer */
+ ndp_offset = le16_to_cpu ( nth->offset );
+ if ( ( ndp_offset + sizeof ( *ndp ) ) > len ) {
+ DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm );
+ rc = -EINVAL;
+ goto error;
+ }
+ ndp = ( iobuf->data + ndp_offset );
+ ndp_len = le16_to_cpu ( ndp->header_len );
+ if ( ndp_len < offsetof ( typeof ( *ndp ), desc ) ) {
+ DBGC ( ncm, "NCM %p NDP header length too short:\n", ncm );
+ rc = -EINVAL;
+ goto error;
+ }
+ if ( ( ndp_offset + ndp_len ) > len ) {
+ DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm );
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /* Process datagrams */
+ remaining = ( ( ndp_len - offsetof ( typeof ( *ndp ), desc ) ) /
+ sizeof ( ndp->desc[0] ) );
+ for ( desc = ndp->desc ; remaining && desc->offset ; remaining-- ) {
+
+ /* Profile individual datagrams */
+ profile_start ( &ncm_in_datagram_profiler );
+
+ /* Locate datagram */
+ pkt_offset = le16_to_cpu ( desc->offset );
+ pkt_len = le16_to_cpu ( desc->len );
+ if ( pkt_len < ETH_HLEN ) {
+ DBGC ( ncm, "NCM %p underlength datagram:\n", ncm );
+ rc = -EINVAL;
+ goto error;
+ }
+ if ( ( pkt_offset + pkt_len ) > len ) {
+ DBGC ( ncm, "NCM %p datagram exceeds packet:\n", ncm );
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /* Move to next descriptor */
+ desc++;
+
+ /* Copy data to a new I/O buffer. Our USB buffers may
+ * be very large and so we choose to recycle the
+ * buffers directly rather than attempt reallocation
+ * while the device is running. We therefore copy the
+ * data to a new I/O buffer even if this is the only
+ * (or last) packet within the buffer.
+ *
+ * We reserve enough space at the start of each buffer
+ * to allow for our own transmission header, to
+ * support protocols such as ARP which may modify the
+ * received packet and reuse the same I/O buffer for
+ * transmission.
+ */
+ headroom = ( sizeof ( struct ncm_ntb_header ) + ncm->padding );
+ pkt = alloc_iob ( headroom + pkt_len );
+ if ( ! pkt ) {
+ /* Record error and continue */
+ netdev_rx_err ( netdev, NULL, -ENOMEM );
+ continue;
+ }
+ iob_reserve ( pkt, headroom );
+ memcpy ( iob_put ( pkt, pkt_len ),
+ ( iobuf->data + pkt_offset ), pkt_len );
+
+ /* Strip CRC, if present */
+ if ( ndp->magic & cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC_CRC))
+ iob_unput ( pkt, 4 /* CRC32 */ );
+
+ /* Hand off to network stack */
+ netdev_rx ( netdev, pkt );
+ profile_stop ( &ncm_in_datagram_profiler );
+ }
+
+ /* Recycle I/O buffer */
+ usb_recycle ( &ncm->usbnet.in, iobuf );
+ profile_stop ( &ncm_in_profiler );
+
+ return;
+
+ error:
+ /* Record error against network device */
+ DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
+ netdev_rx_err ( netdev, NULL, rc );
+ ignore:
+ usb_recycle ( &ncm->usbnet.in, iobuf );
+}
+
+/** Bulk IN endpoint operations */
+static struct usb_endpoint_driver_operations ncm_in_operations = {
+ .complete = ncm_in_complete,
+};
+
+/**
+ * Transmit packet
+ *
+ * @v ncm CDC-NCM device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int ncm_out_transmit ( struct ncm_device *ncm,
+ struct io_buffer *iobuf ) {
+ struct ncm_ntb_header *header;
+ size_t len = iob_len ( iobuf );
+ size_t header_len = ( sizeof ( *header ) + ncm->padding );
+ int rc;
+
+ /* Profile transmissions */
+ profile_start ( &ncm_out_profiler );
+
+ /* Prepend header */
+ if ( ( rc = iob_ensure_headroom ( iobuf, header_len ) ) != 0 )
+ return rc;
+ header = iob_push ( iobuf, header_len );
+
+ /* Populate header */
+ header->nth.magic = cpu_to_le32 ( NCM_TRANSFER_HEADER_MAGIC );
+ header->nth.header_len = cpu_to_le16 ( sizeof ( header->nth ) );
+ header->nth.sequence = cpu_to_le16 ( ncm->sequence );
+ header->nth.len = cpu_to_le16 ( iob_len ( iobuf ) );
+ header->nth.offset =
+ cpu_to_le16 ( offsetof ( typeof ( *header ), ndp ) );
+ header->ndp.magic = cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC );
+ header->ndp.header_len = cpu_to_le16 ( sizeof ( header->ndp ) +
+ sizeof ( header->desc ) );
+ header->ndp.offset = cpu_to_le16 ( 0 );
+ header->desc[0].offset = cpu_to_le16 ( header_len );
+ header->desc[0].len = cpu_to_le16 ( len );
+ memset ( &header->desc[1], 0, sizeof ( header->desc[1] ) );
+
+ /* Enqueue I/O buffer */
+ if ( ( rc = usb_stream ( &ncm->usbnet.out, iobuf, 0 ) ) != 0 )
+ return rc;
+
+ /* Increment sequence number */
+ ncm->sequence++;
+
+ profile_stop ( &ncm_out_profiler );
+ return 0;
+}
+
+/**
+ * Complete bulk OUT transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void ncm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
+ int rc ) {
+ struct ncm_device *ncm = container_of ( ep, struct ncm_device,
+ usbnet.out );
+ struct net_device *netdev = ncm->netdev;
+
+ /* Report TX completion */
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+}
+
+/** Bulk OUT endpoint operations */
+static struct usb_endpoint_driver_operations ncm_out_operations = {
+ .complete = ncm_out_complete,
+};
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int ncm_open ( struct net_device *netdev ) {
+ struct ncm_device *ncm = netdev->priv;
+ struct usb_device *usb = ncm->usb;
+ struct ncm_set_ntb_input_size size;
+ int rc;
+
+ /* Reset sequence number */
+ ncm->sequence = 0;
+
+ /* Prefill I/O buffers */
+ if ( ( rc = ncm_in_prefill ( ncm ) ) != 0 )
+ goto err_prefill;
+
+ /* Set maximum input size */
+ memset ( &size, 0, sizeof ( size ) );
+ size.mtu = cpu_to_le32 ( ncm->usbnet.in.len );
+ if ( ( rc = usb_control ( usb, NCM_SET_NTB_INPUT_SIZE, 0,
+ ncm->usbnet.comms, &size,
+ sizeof ( size ) ) ) != 0 ) {
+ DBGC ( ncm, "NCM %p could not set input size to %zd: %s\n",
+ ncm, ncm->usbnet.in.len, strerror ( rc ) );
+ goto err_set_ntb_input_size;
+ }
+
+ /* Open USB network device */
+ if ( ( rc = usbnet_open ( &ncm->usbnet ) ) != 0 ) {
+ DBGC ( ncm, "NCM %p could not open: %s\n",
+ ncm, strerror ( rc ) );
+ goto err_open;
+ }
+
+ return 0;
+
+ usbnet_close ( &ncm->usbnet );
+ err_open:
+ err_set_ntb_input_size:
+ usb_flush ( &ncm->usbnet.in );
+ err_prefill:
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void ncm_close ( struct net_device *netdev ) {
+ struct ncm_device *ncm = netdev->priv;
+
+ /* Close USB network device */
+ usbnet_close ( &ncm->usbnet );
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int ncm_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct ncm_device *ncm = netdev->priv;
+ int rc;
+
+ /* Transmit packet */
+ if ( ( rc = ncm_out_transmit ( ncm, iobuf ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v netdev Network device
+ */
+static void ncm_poll ( struct net_device *netdev ) {
+ struct ncm_device *ncm = netdev->priv;
+ int rc;
+
+ /* Poll USB bus */
+ usb_poll ( ncm->bus );
+
+ /* Refill endpoints */
+ if ( ( rc = usbnet_refill ( &ncm->usbnet ) ) != 0 )
+ netdev_rx_err ( netdev, NULL, rc );
+
+}
+
+/** CDC-NCM network device operations */
+static struct net_device_operations ncm_operations = {
+ .open = ncm_open,
+ .close = ncm_close,
+ .transmit = ncm_transmit,
+ .poll = ncm_poll,
+};
+
+/******************************************************************************
+ *
+ * USB interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe device
+ *
+ * @v func USB function
+ * @v config Configuration descriptor
+ * @ret rc Return status code
+ */
+static int ncm_probe ( struct usb_function *func,
+ struct usb_configuration_descriptor *config ) {
+ struct usb_device *usb = func->usb;
+ struct net_device *netdev;
+ struct ncm_device *ncm;
+ struct usb_interface_descriptor *comms;
+ struct ecm_ethernet_descriptor *ethernet;
+ struct ncm_ntb_parameters params;
+ int rc;
+
+ /* Allocate and initialise structure */
+ netdev = alloc_etherdev ( sizeof ( *ncm ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &ncm_operations );
+ netdev->dev = &func->dev;
+ ncm = netdev->priv;
+ memset ( ncm, 0, sizeof ( *ncm ) );
+ ncm->usb = usb;
+ ncm->bus = usb->port->hub->bus;
+ ncm->netdev = netdev;
+ usbnet_init ( &ncm->usbnet, func, &ncm_intr_operations,
+ &ncm_in_operations, &ncm_out_operations );
+ usb_refill_init ( &ncm->usbnet.intr, 0, NCM_INTR_COUNT );
+ DBGC ( ncm, "NCM %p on %s\n", ncm, func->name );
+
+ /* Describe USB network device */
+ if ( ( rc = usbnet_describe ( &ncm->usbnet, config ) ) != 0 ) {
+ DBGC ( ncm, "NCM %p could not describe: %s\n",
+ ncm, strerror ( rc ) );
+ goto err_describe;
+ }
+
+ /* Locate Ethernet descriptor */
+ comms = usb_interface_descriptor ( config, ncm->usbnet.comms, 0 );
+ assert ( comms != NULL );
+ ethernet = ecm_ethernet_descriptor ( config, comms );
+ if ( ! ethernet ) {
+ DBGC ( ncm, "NCM %p has no Ethernet descriptor\n", ncm );
+ rc = -EINVAL;
+ goto err_ethernet;
+ }
+
+ /* Fetch MAC address */
+ if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
+ DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n",
+ ncm, strerror ( rc ) );
+ goto err_fetch_mac;
+ }
+
+ /* Get NTB parameters */
+ if ( ( rc = usb_control ( usb, NCM_GET_NTB_PARAMETERS, 0,
+ ncm->usbnet.comms, &params,
+ sizeof ( params ) ) ) != 0 ) {
+ DBGC ( ncm, "NCM %p could not get NTB parameters: %s\n",
+ ncm, strerror ( rc ) );
+ goto err_ntb_parameters;
+ }
+
+ /* Get maximum supported input size */
+ ncm->mtu = le32_to_cpu ( params.in.mtu );
+ DBGC2 ( ncm, "NCM %p maximum IN size is %zd bytes\n", ncm, ncm->mtu );
+
+ /* Calculate transmit padding */
+ ncm->padding = ( ( le16_to_cpu ( params.out.remainder ) -
+ sizeof ( struct ncm_ntb_header ) - ETH_HLEN ) &
+ ( le16_to_cpu ( params.out.divisor ) - 1 ) );
+ DBGC2 ( ncm, "NCM %p using %zd-byte transmit padding\n",
+ ncm, ncm->padding );
+ assert ( ( ( sizeof ( struct ncm_ntb_header ) + ncm->padding +
+ ETH_HLEN ) % le16_to_cpu ( params.out.divisor ) ) ==
+ le16_to_cpu ( params.out.remainder ) );
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+
+ usb_func_set_drvdata ( func, ncm );
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register:
+ err_ntb_parameters:
+ err_fetch_mac:
+ err_ethernet:
+ err_describe:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove device
+ *
+ * @v func USB function
+ */
+static void ncm_remove ( struct usb_function *func ) {
+ struct ncm_device *ncm = usb_func_get_drvdata ( func );
+ struct net_device *netdev = ncm->netdev;
+
+ unregister_netdev ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** CDC-NCM device IDs */
+static struct usb_device_id ncm_ids[] = {
+ {
+ .name = "cdc-ncm",
+ .vendor = USB_ANY_ID,
+ .product = USB_ANY_ID,
+ .class = {
+ .class = USB_CLASS_CDC,
+ .subclass = USB_SUBCLASS_CDC_NCM,
+ .protocol = 0,
+ },
+ },
+};
+
+/** CDC-NCM driver */
+struct usb_driver ncm_driver __usb_driver = {
+ .ids = ncm_ids,
+ .id_count = ( sizeof ( ncm_ids ) / sizeof ( ncm_ids[0] ) ),
+ .probe = ncm_probe,
+ .remove = ncm_remove,
+};
diff --git a/qemu/roms/ipxe/src/drivers/net/ncm.h b/qemu/roms/ipxe/src/drivers/net/ncm.h
new file mode 100644
index 000000000..a9565a56b
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/ncm.h
@@ -0,0 +1,173 @@
+#ifndef _NCM_H
+#define _NCM_H
+
+/** @file
+ *
+ * CDC-NCM USB Ethernet driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/usb.h>
+#include <ipxe/cdc.h>
+#include <byteswap.h>
+#include "ecm.h"
+
+/** CDC-NCM subclass */
+#define USB_SUBCLASS_CDC_NCM 0x0d
+
+/** Get NTB parameters */
+#define NCM_GET_NTB_PARAMETERS \
+ ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \
+ USB_REQUEST_TYPE ( 0x80 ) )
+
+/** NTB datagram parameters */
+struct ncm_ntb_datagram_parameters {
+ /** Maximum size */
+ uint32_t mtu;
+ /** Alignment divisor */
+ uint16_t divisor;
+ /** Alignment remainder */
+ uint16_t remainder;
+ /** Alignment modulus */
+ uint16_t modulus;
+} __attribute__ (( packed ));
+
+/** NTB parameters */
+struct ncm_ntb_parameters {
+ /** Length */
+ uint16_t len;
+ /** Supported formats */
+ uint16_t formats;
+ /** IN datagram parameters */
+ struct ncm_ntb_datagram_parameters in;
+ /** Reserved */
+ uint16_t reserved;
+ /** OUT datagram parameters */
+ struct ncm_ntb_datagram_parameters out;
+ /** Maximum number of datagrams per OUT NTB */
+ uint16_t max;
+} __attribute__ (( packed ));
+
+/** Set NTB input size */
+#define NCM_SET_NTB_INPUT_SIZE \
+ ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \
+ USB_REQUEST_TYPE ( 0x86 ) )
+
+/** Set NTB input size */
+struct ncm_set_ntb_input_size {
+ /** Maximum size */
+ uint32_t mtu;
+} __attribute__ (( packed ));
+
+/** Minimum allowed NTB input size */
+#define NCM_MIN_NTB_INPUT_SIZE 2048
+
+/** Maximum allowed NTB input size (16-bit) */
+#define NCM_MAX_NTB_INPUT_SIZE 65536
+
+/** CDC-NCM transfer header (16-bit) */
+struct ncm_transfer_header {
+ /** Signature */
+ uint32_t magic;
+ /** Header length */
+ uint16_t header_len;
+ /** Sequence number */
+ uint16_t sequence;
+ /** Total length */
+ uint16_t len;
+ /** Offset of first datagram pointer */
+ uint16_t offset;
+} __attribute__ (( packed ));
+
+/** CDC-NCM transfer header magic */
+#define NCM_TRANSFER_HEADER_MAGIC 0x484d434eUL
+
+/** CDC-NCM datagram descriptor (16-bit) */
+struct ncm_datagram_descriptor {
+ /** Starting offset */
+ uint16_t offset;
+ /** Length */
+ uint16_t len;
+} __attribute__ (( packed ));
+
+/** CDC-NCM datagram pointer (16-bit) */
+struct ncm_datagram_pointer {
+ /** Signature */
+ uint32_t magic;
+ /** Header length */
+ uint16_t header_len;
+ /** Offset of next datagram pointer */
+ uint16_t offset;
+ /** Datagram descriptors
+ *
+ * Must be terminated by an empty descriptor.
+ */
+ struct ncm_datagram_descriptor desc[0];
+} __attribute__ (( packed ));
+
+/** CDC-NCM datagram pointer magic */
+#define NCM_DATAGRAM_POINTER_MAGIC 0x304d434eUL
+
+/** CDC-NCM datagram pointer CRC present flag */
+#define NCM_DATAGRAM_POINTER_MAGIC_CRC 0x01000000UL
+
+/** NTB constructed for transmitted packets (excluding padding)
+ *
+ * This is a policy decision.
+ */
+struct ncm_ntb_header {
+ /** Transfer header */
+ struct ncm_transfer_header nth;
+ /** Datagram pointer */
+ struct ncm_datagram_pointer ndp;
+ /** Datagram descriptors */
+ struct ncm_datagram_descriptor desc[2];
+} __attribute__ (( packed ));
+
+/** A CDC-NCM network device */
+struct ncm_device {
+ /** USB device */
+ struct usb_device *usb;
+ /** USB bus */
+ struct usb_bus *bus;
+ /** Network device */
+ struct net_device *netdev;
+ /** USB network device */
+ struct usbnet_device usbnet;
+
+ /** Maximum supported NTB input size */
+ size_t mtu;
+ /** Transmitted packet sequence number */
+ uint16_t sequence;
+ /** Alignment padding required on transmitted packets */
+ size_t padding;
+};
+
+/** Bulk IN ring minimum buffer count
+ *
+ * This is a policy decision.
+ */
+#define NCM_IN_MIN_COUNT 3
+
+/** Bulk IN ring minimum total buffer size
+ *
+ * This is a policy decision.
+ */
+#define NCM_IN_MIN_SIZE 16384
+
+/** Bulk IN ring maximum total buffer size
+ *
+ * This is a policy decision.
+ */
+#define NCM_IN_MAX_SIZE 131072
+
+/** Interrupt ring buffer count
+ *
+ * This is a policy decision.
+ */
+#define NCM_INTR_COUNT 2
+
+#endif /* _NCM_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/netfront.c b/qemu/roms/ipxe/src/drivers/net/netfront.c
index 4b816329e..2f4bbf2a0 100644
--- a/qemu/roms/ipxe/src/drivers/net/netfront.c
+++ b/qemu/roms/ipxe/src/drivers/net/netfront.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
@@ -135,7 +139,7 @@ static int netfront_read_mac ( struct netfront_nic *netfront, void *hw_addr ) {
xendev->key, mac );
/* Decode MAC address */
- len = hex_decode ( mac, ':', hw_addr, ETH_ALEN );
+ len = hex_decode ( ':', mac, hw_addr, ETH_ALEN );
if ( len < 0 ) {
rc = len;
DBGC ( netfront, "NETFRONT %s could not decode MAC address "
@@ -593,6 +597,11 @@ static int netfront_open ( struct net_device *netdev ) {
"feature-no-csum-offload" ) ) != 0 )
goto err_feature_no_csum_offload;
+ /* Inform backend that we will send notifications for RX requests */
+ if ( ( rc = netfront_write_flag ( netfront,
+ "feature-rx-notify" ) ) != 0 )
+ goto err_feature_rx_notify;
+
/* Set state to Connected */
if ( ( rc = xenbus_set_state ( xendev, XenbusStateConnected ) ) != 0 ) {
DBGC ( netfront, "NETFRONT %s could not set state=\"%d\": %s\n",
@@ -618,6 +627,8 @@ static int netfront_open ( struct net_device *netdev ) {
err_backend_wait:
netfront_reset ( netfront );
err_set_state:
+ netfront_rm ( netfront, "feature-rx-notify" );
+ err_feature_rx_notify:
netfront_rm ( netfront, "feature-no-csum-offload" );
err_feature_no_csum_offload:
netfront_rm ( netfront, "request-rx-copy" );
@@ -661,6 +672,7 @@ static void netfront_close ( struct net_device *netdev ) {
}
/* Delete flags */
+ netfront_rm ( netfront, "feature-rx-notify" );
netfront_rm ( netfront, "feature-no-csum-offload" );
netfront_rm ( netfront, "request-rx-copy" );
diff --git a/qemu/roms/ipxe/src/drivers/net/netfront.h b/qemu/roms/ipxe/src/drivers/net/netfront.h
index b3f899f3c..38fd0a77e 100644
--- a/qemu/roms/ipxe/src/drivers/net/netfront.h
+++ b/qemu/roms/ipxe/src/drivers/net/netfront.h
@@ -7,7 +7,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/xen.h>
#include <xen/io/netif.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/netvsc.c b/qemu/roms/ipxe/src/drivers/net/netvsc.c
new file mode 100644
index 000000000..d269cd63e
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/netvsc.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Hyper-V network virtual service client
+ *
+ * The network virtual service client (NetVSC) connects to the network
+ * virtual service provider (NetVSP) via the Hyper-V virtual machine
+ * bus (VMBus). It provides a transport layer for RNDIS packets.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <ipxe/umalloc.h>
+#include <ipxe/rndis.h>
+#include <ipxe/vmbus.h>
+#include "netvsc.h"
+
+/**
+ * Send control message and wait for completion
+ *
+ * @v netvsc NetVSC device
+ * @v xrid Relative transaction ID
+ * @v data Data
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static int netvsc_control ( struct netvsc_device *netvsc, unsigned int xrid,
+ const void *data, size_t len ) {
+ uint64_t xid = ( NETVSC_BASE_XID + xrid );
+ unsigned int i;
+ int rc;
+
+ /* Send control message */
+ if ( ( rc = vmbus_send_control ( netvsc->vmdev, xid, data, len ) ) !=0){
+ DBGC ( netvsc, "NETVSC %s could not send control message: %s\n",
+ netvsc->name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Record transaction ID */
+ netvsc->wait_xrid = xrid;
+
+ /* Wait for operation to complete */
+ for ( i = 0 ; i < NETVSC_MAX_WAIT_MS ; i++ ) {
+
+ /* Check for completion */
+ if ( ! netvsc->wait_xrid )
+ return netvsc->wait_rc;
+
+ /* Poll VMBus device */
+ vmbus_poll ( netvsc->vmdev );
+
+ /* Delay for 1ms */
+ mdelay ( 1 );
+ }
+
+ DBGC ( netvsc, "NETVSC %s timed out waiting for XRID %d\n",
+ netvsc->name, xrid );
+ vmbus_dump_channel ( netvsc->vmdev );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Handle generic completion
+ *
+ * @v netvsc NetVSC device
+ * @v data Data
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static int netvsc_completed ( struct netvsc_device *netvsc __unused,
+ const void *data __unused, size_t len __unused ) {
+ return 0;
+}
+
+/**
+ * Initialise communication
+ *
+ * @v netvsc NetVSC device
+ * @ret rc Return status code
+ */
+static int netvsc_initialise ( struct netvsc_device *netvsc ) {
+ struct netvsc_init_message msg;
+ int rc;
+
+ /* Construct message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.header.type = cpu_to_le32 ( NETVSC_INIT_MSG );
+ msg.min = cpu_to_le32 ( NETVSC_VERSION_1 );
+ msg.max = cpu_to_le32 ( NETVSC_VERSION_1 );
+
+ /* Send message and wait for completion */
+ if ( ( rc = netvsc_control ( netvsc, NETVSC_INIT_XRID, &msg,
+ sizeof ( msg ) ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not initialise: %s\n",
+ netvsc->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle initialisation completion
+ *
+ * @v netvsc NetVSC device
+ * @v data Data
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static int
+netvsc_initialised ( struct netvsc_device *netvsc, const void *data,
+ size_t len ) {
+ const struct netvsc_init_completion *cmplt = data;
+
+ /* Check completion */
+ if ( len < sizeof ( *cmplt ) ) {
+ DBGC ( netvsc, "NETVSC %s underlength initialisation "
+ "completion (%zd bytes)\n", netvsc->name, len );
+ return -EINVAL;
+ }
+ if ( cmplt->header.type != cpu_to_le32 ( NETVSC_INIT_CMPLT ) ) {
+ DBGC ( netvsc, "NETVSC %s unexpected initialisation completion "
+ "type %d\n", netvsc->name,
+ le32_to_cpu ( cmplt->header.type ) );
+ return -EPROTO;
+ }
+ if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
+ DBGC ( netvsc, "NETVSC %s initialisation failure status %d\n",
+ netvsc->name, le32_to_cpu ( cmplt->status ) );
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+/**
+ * Set NDIS version
+ *
+ * @v netvsc NetVSC device
+ * @ret rc Return status code
+ */
+static int netvsc_ndis_version ( struct netvsc_device *netvsc ) {
+ struct netvsc_ndis_version_message msg;
+ int rc;
+
+ /* Construct message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.header.type = cpu_to_le32 ( NETVSC_NDIS_VERSION_MSG );
+ msg.major = cpu_to_le32 ( NETVSC_NDIS_MAJOR );
+ msg.minor = cpu_to_le32 ( NETVSC_NDIS_MINOR );
+
+ /* Send message and wait for completion */
+ if ( ( rc = netvsc_control ( netvsc, NETVSC_NDIS_VERSION_XRID,
+ &msg, sizeof ( msg ) ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not set NDIS version: %s\n",
+ netvsc->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Establish data buffer
+ *
+ * @v netvsc NetVSC device
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int netvsc_establish_buffer ( struct netvsc_device *netvsc,
+ struct netvsc_buffer *buffer ) {
+ struct netvsc_establish_buffer_message msg;
+ int rc;
+
+ /* Construct message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.header.type = cpu_to_le32 ( buffer->establish_type );
+ msg.gpadl = cpu_to_le32 ( buffer->gpadl );
+ msg.pageset = buffer->pages.pageset; /* Already protocol-endian */
+
+ /* Send message and wait for completion */
+ if ( ( rc = netvsc_control ( netvsc, buffer->establish_xrid, &msg,
+ sizeof ( msg ) ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not establish buffer: %s\n",
+ netvsc->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle establish receive data buffer completion
+ *
+ * @v netvsc NetVSC device
+ * @v data Data
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static int netvsc_rx_established_buffer ( struct netvsc_device *netvsc,
+ const void *data, size_t len ) {
+ const struct netvsc_rx_establish_buffer_completion *cmplt = data;
+
+ /* Check completion */
+ if ( len < sizeof ( *cmplt ) ) {
+ DBGC ( netvsc, "NETVSC %s underlength buffer completion (%zd "
+ "bytes)\n", netvsc->name, len );
+ return -EINVAL;
+ }
+ if ( cmplt->header.type != cpu_to_le32 ( NETVSC_RX_ESTABLISH_CMPLT ) ) {
+ DBGC ( netvsc, "NETVSC %s unexpected buffer completion type "
+ "%d\n", netvsc->name, le32_to_cpu ( cmplt->header.type));
+ return -EPROTO;
+ }
+ if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) {
+ DBGC ( netvsc, "NETVSC %s buffer failure status %d\n",
+ netvsc->name, le32_to_cpu ( cmplt->status ) );
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+/**
+ * Revoke data buffer
+ *
+ * @v netvsc NetVSC device
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int netvsc_revoke_buffer ( struct netvsc_device *netvsc,
+ struct netvsc_buffer *buffer ) {
+ struct netvsc_revoke_buffer_message msg;
+ int rc;
+
+ /* Construct message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.header.type = cpu_to_le32 ( buffer->revoke_type );
+ msg.pageset = buffer->pages.pageset; /* Already protocol-endian */
+
+ /* Send message and wait for completion */
+ if ( ( rc = netvsc_control ( netvsc, buffer->revoke_xrid,
+ &msg, sizeof ( msg ) ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not revoke buffer: %s\n",
+ netvsc->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle received control packet
+ *
+ * @v vmdev VMBus device
+ * @v xid Transaction ID
+ * @v data Data
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid,
+ const void *data, size_t len ) {
+ struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
+ struct netvsc_device *netvsc = rndis->priv;
+
+ DBGC ( netvsc, "NETVSC %s received unsupported control packet "
+ "(%08llx):\n", netvsc->name, xid );
+ DBGC_HDA ( netvsc, 0, data, len );
+ return -ENOTSUP;
+}
+
+/**
+ * Handle received data packet
+ *
+ * @v vmdev VMBus device
+ * @v xid Transaction ID
+ * @v data Data
+ * @v len Length of data
+ * @v list List of I/O buffers
+ * @ret rc Return status code
+ */
+static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
+ const void *data, size_t len,
+ struct list_head *list ) {
+ struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
+ struct netvsc_device *netvsc = rndis->priv;
+ const struct netvsc_rndis_message *msg = data;
+ struct io_buffer *iobuf;
+ struct io_buffer *tmp;
+ int rc;
+
+ /* Sanity check */
+ if ( len < sizeof ( *msg ) ) {
+ DBGC ( netvsc, "NETVSC %s received underlength RNDIS packet "
+ "(%zd bytes)\n", netvsc->name, len );
+ rc = -EINVAL;
+ goto err_sanity;
+ }
+ if ( msg->header.type != cpu_to_le32 ( NETVSC_RNDIS_MSG ) ) {
+ DBGC ( netvsc, "NETVSC %s received unexpected RNDIS packet "
+ "type %d\n", netvsc->name,
+ le32_to_cpu ( msg->header.type ) );
+ rc = -EINVAL;
+ goto err_sanity;
+ }
+
+ /* Send completion back to host */
+ if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
+ netvsc->name, strerror ( rc ) );
+ goto err_completion;
+ }
+
+ /* Hand off to RNDIS */
+ list_for_each_entry_safe ( iobuf, tmp, list, list ) {
+ list_del ( &iobuf->list );
+ rndis_rx ( rndis, iob_disown ( iobuf ) );
+ }
+
+ return 0;
+
+ err_completion:
+ err_sanity:
+ list_for_each_entry_safe ( iobuf, tmp, list, list ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
+ return rc;
+}
+
+/**
+ * Handle received completion packet
+ *
+ * @v vmdev VMBus device
+ * @v xid Transaction ID
+ * @v data Data
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static int netvsc_recv_completion ( struct vmbus_device *vmdev, uint64_t xid,
+ const void *data, size_t len ) {
+ struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
+ struct netvsc_device *netvsc = rndis->priv;
+ struct io_buffer *iobuf;
+ int ( * completion ) ( struct netvsc_device *netvsc,
+ const void *data, size_t len );
+ unsigned int xrid = ( xid - NETVSC_BASE_XID );
+ unsigned int tx_id;
+ int rc;
+
+ /* Handle transmit completion, if applicable */
+ tx_id = ( xrid - NETVSC_TX_BASE_XRID );
+ if ( ( tx_id < NETVSC_TX_NUM_DESC ) &&
+ ( ( iobuf = netvsc->tx.iobufs[tx_id] ) != NULL ) ) {
+
+ /* Free buffer ID */
+ netvsc->tx.iobufs[tx_id] = NULL;
+ netvsc->tx.ids[ ( netvsc->tx.id_cons++ ) &
+ ( netvsc->tx.count - 1 ) ] = tx_id;
+
+ /* Hand back to RNDIS */
+ rndis_tx_complete ( rndis, iobuf );
+ return 0;
+ }
+
+ /* Otherwise determine completion handler */
+ if ( xrid == NETVSC_INIT_XRID ) {
+ completion = netvsc_initialised;
+ } else if ( xrid == NETVSC_RX_ESTABLISH_XRID ) {
+ completion = netvsc_rx_established_buffer;
+ } else if ( ( netvsc->wait_xrid != 0 ) &&
+ ( xrid == netvsc->wait_xrid ) ) {
+ completion = netvsc_completed;
+ } else {
+ DBGC ( netvsc, "NETVSC %s received unexpected completion "
+ "(%08llx)\n", netvsc->name, xid );
+ return -EPIPE;
+ }
+
+ /* Hand off to completion handler */
+ rc = completion ( netvsc, data, len );
+
+ /* Record completion handler result if applicable */
+ if ( xrid == netvsc->wait_xrid ) {
+ netvsc->wait_xrid = 0;
+ netvsc->wait_rc = rc;
+ }
+
+ return rc;
+}
+
+/**
+ * Handle received cancellation packet
+ *
+ * @v vmdev VMBus device
+ * @v xid Transaction ID
+ * @ret rc Return status code
+ */
+static int netvsc_recv_cancellation ( struct vmbus_device *vmdev,
+ uint64_t xid ) {
+ struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
+ struct netvsc_device *netvsc = rndis->priv;
+
+ DBGC ( netvsc, "NETVSC %s received unsupported cancellation packet "
+ "(%08llx):\n", netvsc->name, xid );
+ return -ENOTSUP;
+}
+
+/** VMBus channel operations */
+static struct vmbus_channel_operations netvsc_channel_operations = {
+ .recv_control = netvsc_recv_control,
+ .recv_data = netvsc_recv_data,
+ .recv_completion = netvsc_recv_completion,
+ .recv_cancellation = netvsc_recv_cancellation,
+};
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v rndis RNDIS device
+ */
+static void netvsc_poll ( struct rndis_device *rndis ) {
+ struct netvsc_device *netvsc = rndis->priv;
+ struct vmbus_device *vmdev = netvsc->vmdev;
+
+ /* Poll VMBus device */
+ while ( vmbus_has_data ( vmdev ) )
+ vmbus_poll ( vmdev );
+}
+
+/**
+ * Transmit packet
+ *
+ * @v rndis RNDIS device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ *
+ * If this method returns success then the RNDIS device must
+ * eventually report completion via rndis_tx_complete().
+ */
+static int netvsc_transmit ( struct rndis_device *rndis,
+ struct io_buffer *iobuf ) {
+ struct netvsc_device *netvsc = rndis->priv;
+ struct rndis_header *header = iobuf->data;
+ struct netvsc_rndis_message msg;
+ unsigned int tx_id;
+ unsigned int xrid;
+ uint64_t xid;
+ int rc;
+
+ /* Sanity check */
+ assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
+ assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
+
+ /* Check that we have space in the transmit ring */
+ if ( netvsc_ring_is_full ( &netvsc->tx ) )
+ return rndis_tx_defer ( rndis, iobuf );
+
+ /* Allocate buffer ID and calculate transaction ID */
+ tx_id = netvsc->tx.ids[ netvsc->tx.id_prod & ( netvsc->tx.count - 1 ) ];
+ assert ( netvsc->tx.iobufs[tx_id] == NULL );
+ xrid = ( NETVSC_TX_BASE_XRID + tx_id );
+ xid = ( NETVSC_BASE_XID + xrid );
+
+ /* Construct message */
+ memset ( &msg, 0, sizeof ( msg ) );
+ msg.header.type = cpu_to_le32 ( NETVSC_RNDIS_MSG );
+ msg.channel = ( ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) ?
+ NETVSC_RNDIS_DATA : NETVSC_RNDIS_CONTROL );
+ msg.buffer = cpu_to_le32 ( NETVSC_RNDIS_NO_BUFFER );
+
+ /* Send message */
+ if ( ( rc = vmbus_send_data ( netvsc->vmdev, xid, &msg, sizeof ( msg ),
+ iobuf ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not send RNDIS message: %s\n",
+ netvsc->name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Store I/O buffer and consume buffer ID */
+ netvsc->tx.iobufs[tx_id] = iobuf;
+ netvsc->tx.id_prod++;
+
+ return 0;
+}
+
+/**
+ * Cancel transmission
+ *
+ * @v netvsc NetVSC device
+ * @v iobuf I/O buffer
+ * @v tx_id Transmission ID
+ */
+static void netvsc_cancel_transmit ( struct netvsc_device *netvsc,
+ struct io_buffer *iobuf,
+ unsigned int tx_id ) {
+ unsigned int xrid;
+ uint64_t xid;
+
+ /* Send cancellation */
+ xrid = ( NETVSC_TX_BASE_XRID + tx_id );
+ xid = ( NETVSC_BASE_XID + xrid );
+ DBGC ( netvsc, "NETVSC %s cancelling transmission %#x\n",
+ netvsc->name, tx_id );
+ vmbus_send_cancellation ( netvsc->vmdev, xid );
+
+ /* Report back to RNDIS */
+ rndis_tx_complete_err ( netvsc->rndis, iobuf, -ECANCELED );
+}
+
+/**
+ * Create descriptor ring
+ *
+ * @v netvsc NetVSC device
+ * @v ring Descriptor ring
+ * @ret rc Return status code
+ */
+static int netvsc_create_ring ( struct netvsc_device *netvsc __unused,
+ struct netvsc_ring *ring ) {
+ unsigned int i;
+
+ /* Initialise buffer ID ring */
+ for ( i = 0 ; i < ring->count ; i++ ) {
+ ring->ids[i] = i;
+ assert ( ring->iobufs[i] == NULL );
+ }
+ ring->id_prod = 0;
+ ring->id_cons = 0;
+
+ return 0;
+}
+
+/**
+ * Destroy descriptor ring
+ *
+ * @v netvsc NetVSC device
+ * @v ring Descriptor ring
+ * @v discard Method used to discard outstanding buffer, or NULL
+ */
+static void netvsc_destroy_ring ( struct netvsc_device *netvsc,
+ struct netvsc_ring *ring,
+ void ( * discard ) ( struct netvsc_device *,
+ struct io_buffer *,
+ unsigned int ) ) {
+ struct io_buffer *iobuf;
+ unsigned int i;
+
+ /* Flush any outstanding buffers */
+ for ( i = 0 ; i < ring->count ; i++ ) {
+ iobuf = ring->iobufs[i];
+ if ( ! iobuf )
+ continue;
+ ring->iobufs[i] = NULL;
+ ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = i;
+ if ( discard )
+ discard ( netvsc, iobuf, i );
+ }
+
+ /* Sanity check */
+ assert ( netvsc_ring_is_empty ( ring ) );
+}
+
+/**
+ * Copy data from data buffer
+ *
+ * @v pages Transfer page set
+ * @v data Data buffer
+ * @v offset Offset within page set
+ * @v len Length within page set
+ * @ret rc Return status code
+ */
+static int netvsc_buffer_copy ( struct vmbus_xfer_pages *pages, void *data,
+ size_t offset, size_t len ) {
+ struct netvsc_buffer *buffer =
+ container_of ( pages, struct netvsc_buffer, pages );
+
+ /* Sanity check */
+ if ( ( offset > buffer->len ) || ( len > ( buffer->len - offset ) ) )
+ return -ERANGE;
+
+ /* Copy data from buffer */
+ copy_from_user ( data, buffer->data, offset, len );
+
+ return 0;
+}
+
+/** Transfer page set operations */
+static struct vmbus_xfer_pages_operations netvsc_xfer_pages_operations = {
+ .copy = netvsc_buffer_copy,
+};
+
+/**
+ * Create data buffer
+ *
+ * @v netvsc NetVSC device
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int netvsc_create_buffer ( struct netvsc_device *netvsc,
+ struct netvsc_buffer *buffer ) {
+ struct vmbus_device *vmdev = netvsc->vmdev;
+ int gpadl;
+ int rc;
+
+ /* Allocate receive buffer */
+ buffer->data = umalloc ( buffer->len );
+ if ( ! buffer->data ) {
+ DBGC ( netvsc, "NETVSC %s could not allocate %zd-byte buffer\n",
+ netvsc->name, buffer->len );
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Establish GPA descriptor list */
+ gpadl = vmbus_establish_gpadl ( vmdev, buffer->data, buffer->len );
+ if ( gpadl < 0 ) {
+ rc = gpadl;
+ DBGC ( netvsc, "NETVSC %s could not establish GPADL: %s\n",
+ netvsc->name, strerror ( rc ) );
+ goto err_establish_gpadl;
+ }
+ buffer->gpadl = gpadl;
+
+ /* Register transfer page set */
+ if ( ( rc = vmbus_register_pages ( vmdev, &buffer->pages ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not register transfer pages: "
+ "%s\n", netvsc->name, strerror ( rc ) );
+ goto err_register_pages;
+ }
+
+ return 0;
+
+ vmbus_unregister_pages ( vmdev, &buffer->pages );
+ err_register_pages:
+ vmbus_gpadl_teardown ( vmdev, gpadl );
+ err_establish_gpadl:
+ ufree ( buffer->data );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Destroy data buffer
+ *
+ * @v netvsc NetVSC device
+ * @v buffer Data buffer
+ */
+static void netvsc_destroy_buffer ( struct netvsc_device *netvsc,
+ struct netvsc_buffer *buffer ) {
+ struct vmbus_device *vmdev = netvsc->vmdev;
+ int rc;
+
+ /* Unregister transfer pages */
+ vmbus_unregister_pages ( vmdev, &buffer->pages );
+
+ /* Tear down GPA descriptor list */
+ if ( ( rc = vmbus_gpadl_teardown ( vmdev, buffer->gpadl ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not tear down GPADL: %s\n",
+ netvsc->name, strerror ( rc ) );
+ /* Death is imminent. The host may well continue to
+ * write to the data buffer. The best we can do is
+ * leak memory for now and hope that the host doesn't
+ * write to this region after we load an OS.
+ */
+ return;
+ }
+
+ /* Free buffer */
+ ufree ( buffer->data );
+}
+
+/**
+ * Open device
+ *
+ * @v rndis RNDIS device
+ * @ret rc Return status code
+ */
+static int netvsc_open ( struct rndis_device *rndis ) {
+ struct netvsc_device *netvsc = rndis->priv;
+ int rc;
+
+ /* Initialise receive buffer */
+ if ( ( rc = netvsc_create_buffer ( netvsc, &netvsc->rx ) ) != 0 )
+ goto err_create_rx;
+
+ /* Open channel */
+ if ( ( rc = vmbus_open ( netvsc->vmdev, &netvsc_channel_operations,
+ PAGE_SIZE, PAGE_SIZE, NETVSC_MTU ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not open VMBus: %s\n",
+ netvsc->name, strerror ( rc ) );
+ goto err_vmbus_open;
+ }
+
+ /* Initialise communication with NetVSP */
+ if ( ( rc = netvsc_initialise ( netvsc ) ) != 0 )
+ goto err_initialise;
+ if ( ( rc = netvsc_ndis_version ( netvsc ) ) != 0 )
+ goto err_ndis_version;
+
+ /* Initialise transmit ring */
+ if ( ( rc = netvsc_create_ring ( netvsc, &netvsc->tx ) ) != 0 )
+ goto err_create_tx;
+
+ /* Establish receive buffer */
+ if ( ( rc = netvsc_establish_buffer ( netvsc, &netvsc->rx ) ) != 0 )
+ goto err_establish_rx;
+
+ return 0;
+
+ netvsc_revoke_buffer ( netvsc, &netvsc->rx );
+ err_establish_rx:
+ netvsc_destroy_ring ( netvsc, &netvsc->tx, NULL );
+ err_create_tx:
+ err_ndis_version:
+ err_initialise:
+ vmbus_close ( netvsc->vmdev );
+ err_vmbus_open:
+ netvsc_destroy_buffer ( netvsc, &netvsc->rx );
+ err_create_rx:
+ return rc;
+}
+
+/**
+ * Close device
+ *
+ * @v rndis RNDIS device
+ */
+static void netvsc_close ( struct rndis_device *rndis ) {
+ struct netvsc_device *netvsc = rndis->priv;
+
+ /* Revoke receive buffer */
+ netvsc_revoke_buffer ( netvsc, &netvsc->rx );
+
+ /* Destroy transmit ring */
+ netvsc_destroy_ring ( netvsc, &netvsc->tx, netvsc_cancel_transmit );
+
+ /* Close channel */
+ vmbus_close ( netvsc->vmdev );
+
+ /* Destroy receive buffer */
+ netvsc_destroy_buffer ( netvsc, &netvsc->rx );
+}
+
+/** RNDIS operations */
+static struct rndis_operations netvsc_operations = {
+ .open = netvsc_open,
+ .close = netvsc_close,
+ .transmit = netvsc_transmit,
+ .poll = netvsc_poll,
+};
+
+/**
+ * Probe device
+ *
+ * @v vmdev VMBus device
+ * @ret rc Return status code
+ */
+static int netvsc_probe ( struct vmbus_device *vmdev ) {
+ struct netvsc_device *netvsc;
+ struct rndis_device *rndis;
+ int rc;
+
+ /* Allocate and initialise structure */
+ rndis = alloc_rndis ( sizeof ( *netvsc ) );
+ if ( ! rndis ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ rndis_init ( rndis, &netvsc_operations );
+ rndis->netdev->dev = &vmdev->dev;
+ netvsc = rndis->priv;
+ netvsc->vmdev = vmdev;
+ netvsc->rndis = rndis;
+ netvsc->name = vmdev->dev.name;
+ netvsc_init_ring ( &netvsc->tx, NETVSC_TX_NUM_DESC,
+ netvsc->tx_iobufs, netvsc->tx_ids );
+ netvsc_init_buffer ( &netvsc->rx, NETVSC_RX_BUF_PAGESET,
+ &netvsc_xfer_pages_operations,
+ NETVSC_RX_ESTABLISH_MSG, NETVSC_RX_ESTABLISH_XRID,
+ NETVSC_RX_REVOKE_MSG, NETVSC_RX_REVOKE_XRID,
+ NETVSC_RX_BUF_LEN );
+ vmbus_set_drvdata ( vmdev, rndis );
+
+ /* Register RNDIS device */
+ if ( ( rc = register_rndis ( rndis ) ) != 0 ) {
+ DBGC ( netvsc, "NETVSC %s could not register: %s\n",
+ netvsc->name, strerror ( rc ) );
+ goto err_register;
+ }
+
+ return 0;
+
+ unregister_rndis ( rndis );
+ err_register:
+ free_rndis ( rndis );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove device
+ *
+ * @v vmdev VMBus device
+ */
+static void netvsc_remove ( struct vmbus_device *vmdev ) {
+ struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
+
+ /* Unregister RNDIS device */
+ unregister_rndis ( rndis );
+
+ /* Free RNDIS device */
+ free_rndis ( rndis );
+}
+
+/** NetVSC driver */
+struct vmbus_driver netvsc_driver __vmbus_driver = {
+ .name = "netvsc",
+ .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f,
+ 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ),
+ .probe = netvsc_probe,
+ .remove = netvsc_remove,
+};
diff --git a/qemu/roms/ipxe/src/drivers/net/netvsc.h b/qemu/roms/ipxe/src/drivers/net/netvsc.h
new file mode 100644
index 000000000..39eeb891c
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/netvsc.h
@@ -0,0 +1,365 @@
+#ifndef _NETVSC_H
+#define _NETVSC_H
+
+/** @file
+ *
+ * Hyper-V network virtual service client
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** Maximum supported NetVSC message length */
+#define NETVSC_MTU 512
+
+/** Maximum time to wait for a transaction to complete
+ *
+ * This is a policy decision.
+ */
+#define NETVSC_MAX_WAIT_MS 1000
+
+/** Number of transmit ring entries
+ *
+ * Must be a power of two. This is a policy decision. This value
+ * must be sufficiently small to guarantee that we never run out of
+ * space in the VMBus outbound ring buffer.
+ */
+#define NETVSC_TX_NUM_DESC 32
+
+/** RX data buffer page set ID
+ *
+ * This is a policy decision.
+ */
+#define NETVSC_RX_BUF_PAGESET 0xbead
+
+/** RX data buffer length
+ *
+ * This is a policy decision.
+ */
+#define NETVSC_RX_BUF_LEN ( 16 * PAGE_SIZE )
+
+/** Base transaction ID
+ *
+ * This is a policy decision.
+ */
+#define NETVSC_BASE_XID 0x18ae0000UL
+
+/** Relative transaction IDs */
+enum netvsc_xrid {
+ /** Transmit descriptors (one per transmit buffer ID) */
+ NETVSC_TX_BASE_XRID = 0,
+ /** Initialisation */
+ NETVSC_INIT_XRID = ( NETVSC_TX_BASE_XRID + NETVSC_TX_NUM_DESC ),
+ /** NDIS version */
+ NETVSC_NDIS_VERSION_XRID,
+ /** Establish receive buffer */
+ NETVSC_RX_ESTABLISH_XRID,
+ /** Revoke receive buffer */
+ NETVSC_RX_REVOKE_XRID,
+};
+
+/** NetVSC status codes */
+enum netvsc_status {
+ NETVSC_NONE = 0,
+ NETVSC_OK = 1,
+ NETVSC_FAIL = 2,
+ NETVSC_TOO_NEW = 3,
+ NETVSC_TOO_OLD = 4,
+ NETVSC_BAD_PACKET = 5,
+ NETVSC_BUSY = 6,
+ NETVSC_UNSUPPORTED = 7,
+};
+
+/** NetVSC message header */
+struct netvsc_header {
+ /** Type */
+ uint32_t type;
+} __attribute__ (( packed ));
+
+/** NetVSC initialisation message */
+#define NETVSC_INIT_MSG 1
+
+/** NetVSC initialisation message */
+struct netvsc_init_message {
+ /** Message header */
+ struct netvsc_header header;
+ /** Minimum supported protocol version */
+ uint32_t min;
+ /** Maximum supported protocol version */
+ uint32_t max;
+ /** Reserved */
+ uint8_t reserved[20];
+} __attribute__ (( packed ));
+
+/** Oldest known NetVSC protocol version */
+#define NETVSC_VERSION_1 2 /* sic */
+
+/** NetVSC initialisation completion */
+#define NETVSC_INIT_CMPLT 2
+
+/** NetVSC initialisation completion */
+struct netvsc_init_completion {
+ /** Message header */
+ struct netvsc_header header;
+ /** Protocol version */
+ uint32_t version;
+ /** Maximum memory descriptor list length */
+ uint32_t max_mdl_len;
+ /** Status */
+ uint32_t status;
+ /** Reserved */
+ uint8_t reserved[16];
+} __attribute__ (( packed ));
+
+/** NetVSC NDIS version message */
+#define NETVSC_NDIS_VERSION_MSG 100
+
+/** NetVSC NDIS version message */
+struct netvsc_ndis_version_message {
+ /** Message header */
+ struct netvsc_header header;
+ /** Major version */
+ uint32_t major;
+ /** Minor version */
+ uint32_t minor;
+ /** Reserved */
+ uint8_t reserved[20];
+} __attribute__ (( packed ));
+
+/** NetVSC NDIS major version */
+#define NETVSC_NDIS_MAJOR 6
+
+/** NetVSC NDIS minor version */
+#define NETVSC_NDIS_MINOR 1
+
+/** NetVSC establish receive data buffer message */
+#define NETVSC_RX_ESTABLISH_MSG 101
+
+/** NetVSC establish receive data buffer completion */
+#define NETVSC_RX_ESTABLISH_CMPLT 102
+
+/** NetVSC revoke receive data buffer message */
+#define NETVSC_RX_REVOKE_MSG 103
+
+/** NetVSC establish transmit data buffer message */
+#define NETVSC_TX_ESTABLISH_MSG 104
+
+/** NetVSC establish transmit data buffer completion */
+#define NETVSC_TX_ESTABLISH_CMPLT 105
+
+/** NetVSC revoke transmit data buffer message */
+#define NETVSC_TX_REVOKE_MSG 106
+
+/** NetVSC establish data buffer message */
+struct netvsc_establish_buffer_message {
+ /** Message header */
+ struct netvsc_header header;
+ /** GPADL ID */
+ uint32_t gpadl;
+ /** Page set ID */
+ uint16_t pageset;
+ /** Reserved */
+ uint8_t reserved[22];
+} __attribute__ (( packed ));
+
+/** NetVSC receive data buffer section */
+struct netvsc_rx_buffer_section {
+ /** Starting offset */
+ uint32_t start;
+ /** Subsection length */
+ uint32_t len;
+ /** Number of subsections */
+ uint32_t count;
+ /** Ending offset */
+ uint32_t end;
+} __attribute__ (( packed ));
+
+/** NetVSC establish receive data buffer completion */
+struct netvsc_rx_establish_buffer_completion {
+ /** Message header */
+ struct netvsc_header header;
+ /** Status */
+ uint32_t status;
+ /** Number of sections (must be 1) */
+ uint32_t count;
+ /** Section descriptors */
+ struct netvsc_rx_buffer_section section[1];
+} __attribute__ (( packed ));
+
+/** NetVSC establish transmit data buffer completion */
+struct netvsc_tx_establish_buffer_completion {
+ /** Message header */
+ struct netvsc_header header;
+ /** Status */
+ uint32_t status;
+ /** Section length */
+ uint32_t len;
+} __attribute__ (( packed ));
+
+/** NetVSC revoke data buffer message */
+struct netvsc_revoke_buffer_message {
+ /** Message header */
+ struct netvsc_header header;
+ /** Page set ID */
+ uint16_t pageset;
+ /** Reserved */
+ uint8_t reserved[26];
+} __attribute__ (( packed ));
+
+/** NetVSC RNDIS message */
+#define NETVSC_RNDIS_MSG 107
+
+/** NetVSC RNDIS message */
+struct netvsc_rndis_message {
+ /** Message header */
+ struct netvsc_header header;
+ /** RNDIS channel */
+ uint32_t channel;
+ /** Buffer index (or NETVSC_RNDIS_NO_BUFFER) */
+ uint32_t buffer;
+ /** Buffer length */
+ uint32_t len;
+ /** Reserved */
+ uint8_t reserved[16];
+} __attribute__ (( packed ));
+
+/** RNDIS data channel (for RNDIS_PACKET_MSG only) */
+#define NETVSC_RNDIS_DATA 0
+
+/** RNDIS control channel (for all other RNDIS messages) */
+#define NETVSC_RNDIS_CONTROL 1
+
+/** "No buffer used" index */
+#define NETVSC_RNDIS_NO_BUFFER 0xffffffffUL
+
+/** A NetVSC descriptor ring */
+struct netvsc_ring {
+ /** Number of descriptors */
+ unsigned int count;
+ /** I/O buffers, indexed by buffer ID */
+ struct io_buffer **iobufs;
+ /** Buffer ID ring */
+ uint8_t *ids;
+ /** Buffer ID producer counter */
+ unsigned int id_prod;
+ /** Buffer ID consumer counter */
+ unsigned int id_cons;
+};
+
+/**
+ * Initialise descriptor ring
+ *
+ * @v ring Descriptor ring
+ * @v count Maximum number of used descriptors
+ * @v iobufs I/O buffers
+ * @v ids Buffer IDs
+ */
+static inline __attribute__ (( always_inline )) void
+netvsc_init_ring ( struct netvsc_ring *ring, unsigned int count,
+ struct io_buffer **iobufs, uint8_t *ids ) {
+
+ ring->count = count;
+ ring->iobufs = iobufs;
+ ring->ids = ids;
+}
+
+/**
+ * Check whether or not descriptor ring is full
+ *
+ * @v ring Descriptor ring
+ * @v is_full Ring is full
+ */
+static inline __attribute__ (( always_inline )) int
+netvsc_ring_is_full ( struct netvsc_ring *ring ) {
+ unsigned int fill_level;
+
+ fill_level = ( ring->id_prod - ring->id_cons );
+ assert ( fill_level <= ring->count );
+ return ( fill_level >= ring->count );
+}
+
+/**
+ * Check whether or not descriptor ring is empty
+ *
+ * @v ring Descriptor ring
+ * @v is_empty Ring is empty
+ */
+static inline __attribute__ (( always_inline )) int
+netvsc_ring_is_empty ( struct netvsc_ring *ring ) {
+
+ return ( ring->id_prod == ring->id_cons );
+}
+
+/** A NetVSC data buffer */
+struct netvsc_buffer {
+ /** Transfer page set */
+ struct vmbus_xfer_pages pages;
+ /** Establish data buffer message type */
+ uint8_t establish_type;
+ /** Establish data buffer relative transaction ID */
+ uint8_t establish_xrid;
+ /** Revoke data buffer message type */
+ uint8_t revoke_type;
+ /** Revoke data buffer relative transaction ID */
+ uint8_t revoke_xrid;
+ /** Buffer length */
+ size_t len;
+ /** Buffer */
+ userptr_t data;
+ /** GPADL ID */
+ unsigned int gpadl;
+};
+
+/**
+ * Initialise data buffer
+ *
+ * @v buffer Data buffer
+ * @v pageset Page set ID
+ * @v op Page set operations
+ * @v establish_type Establish data buffer message type
+ * @v establish_xrid Establish data buffer relative transaction ID
+ * @v revoke_type Revoke data buffer message type
+ * @v revoke_type Revoke data buffer relative transaction ID
+ * @v len Required length
+ */
+static inline __attribute__ (( always_inline )) void
+netvsc_init_buffer ( struct netvsc_buffer *buffer, uint16_t pageset,
+ struct vmbus_xfer_pages_operations *op,
+ uint8_t establish_type, uint8_t establish_xrid,
+ uint8_t revoke_type, uint8_t revoke_xrid, size_t len ) {
+
+ buffer->pages.pageset = cpu_to_le16 ( pageset );
+ buffer->pages.op = op;
+ buffer->establish_type = establish_type;
+ buffer->establish_xrid = establish_xrid;
+ buffer->revoke_type = revoke_type;
+ buffer->revoke_xrid = revoke_xrid;
+ buffer->len = len;
+}
+
+/** A NetVSC device */
+struct netvsc_device {
+ /** VMBus device */
+ struct vmbus_device *vmdev;
+ /** RNDIS device */
+ struct rndis_device *rndis;
+ /** Name */
+ const char *name;
+
+ /** Transmit ring */
+ struct netvsc_ring tx;
+ /** Transmit buffer IDs */
+ uint8_t tx_ids[NETVSC_TX_NUM_DESC];
+ /** Transmit I/O buffers */
+ struct io_buffer *tx_iobufs[NETVSC_TX_NUM_DESC];
+
+ /** Receive buffer */
+ struct netvsc_buffer rx;
+
+ /** Relative transaction ID for current blocking transaction */
+ unsigned int wait_xrid;
+ /** Return status code for current blocking transaction */
+ int wait_rc;
+};
+
+#endif /* _NETVSC_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h b/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h
index 15f3d3767..1687b6952 100644
--- a/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h
+++ b/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h
@@ -18,9 +18,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c
index e70ded08c..38b66743c 100644
--- a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c
+++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c
@@ -16,9 +16,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h
index 1647168ba..967603409 100644
--- a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h
+++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h
@@ -19,9 +19,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h b/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h
index 7dfff52b2..016730de3 100644
--- a/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h
+++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h
@@ -19,9 +19,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
diff --git a/qemu/roms/ipxe/src/drivers/net/pnic.c b/qemu/roms/ipxe/src/drivers/net/pnic.c
index 4170cc640..ca64299ea 100644
--- a/qemu/roms/ipxe/src/drivers/net/pnic.c
+++ b/qemu/roms/ipxe/src/drivers/net/pnic.c
@@ -6,8 +6,18 @@ Bochs Pseudo NIC driver for Etherboot
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*
* See pnic_api.h for an explanation of the Bochs Pseudo NIC.
*/
diff --git a/qemu/roms/ipxe/src/drivers/net/prism2.c b/qemu/roms/ipxe/src/drivers/net/prism2.c
index ab974264c..4331f2cd0 100644
--- a/qemu/roms/ipxe/src/drivers/net/prism2.c
+++ b/qemu/roms/ipxe/src/drivers/net/prism2.c
@@ -9,8 +9,18 @@ $Id$
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
diff --git a/qemu/roms/ipxe/src/drivers/net/prism2_pci.c b/qemu/roms/ipxe/src/drivers/net/prism2_pci.c
index 72549babf..69ddf0fb0 100644
--- a/qemu/roms/ipxe/src/drivers/net/prism2_pci.c
+++ b/qemu/roms/ipxe/src/drivers/net/prism2_pci.c
@@ -10,8 +10,18 @@ $Id$
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
@@ -40,8 +50,6 @@ static void prism2_pci_disable ( struct nic *nic ) {
static struct pci_device_id prism2_pci_nics[] = {
PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone", 0),
-PCI_ROM(0x1260, 0x3873, "hwp01170", "ActionTec HWP01170", 0),
-PCI_ROM(0x1260, 0x3873, "dwl520", "DLink DWL-520", 0),
};
PCI_DRIVER ( prism2_pci_driver, prism2_pci_nics, PCI_NO_CLASS );
diff --git a/qemu/roms/ipxe/src/drivers/net/prism2_plx.c b/qemu/roms/ipxe/src/drivers/net/prism2_plx.c
index 2098f7f09..a73b0e087 100644
--- a/qemu/roms/ipxe/src/drivers/net/prism2_plx.c
+++ b/qemu/roms/ipxe/src/drivers/net/prism2_plx.c
@@ -10,8 +10,18 @@ $Id$
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
@@ -44,10 +54,10 @@ static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p )
/* Obtain all memory and IO base addresses */
pci_read_config_dword( p, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr);
- plx_lcr &= PCI_BASE_ADDRESS_IO_MASK;
+ plx_lcr &= ~PCI_BASE_ADDRESS_IO_MASK;
pci_read_config_dword( p, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem);
pci_read_config_dword( p, PRISM2_PLX_IO_BASE, &iobase);
- iobase &= PCI_BASE_ADDRESS_IO_MASK;
+ iobase &= ~PCI_BASE_ADDRESS_IO_MASK;
/* Fill out hw structure */
hw->iobase = iobase;
diff --git a/qemu/roms/ipxe/src/drivers/net/realtek.c b/qemu/roms/ipxe/src/drivers/net/realtek.c
index 0aca8c77f..022b59324 100644
--- a/qemu/roms/ipxe/src/drivers/net/realtek.c
+++ b/qemu/roms/ipxe/src/drivers/net/realtek.c
@@ -17,9 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
@@ -194,7 +198,6 @@ static int realtek_init_eeprom ( struct net_device *netdev ) {
DBGC ( rtl, "REALTEK %p EEPROM is a 93C46\n", rtl );
init_at93c46 ( &rtl->eeprom, 16 );
}
- rtl->eeprom.bus = &rtl->spibit.bus;
/* Check for EEPROM presence. Some onboard NICs will have no
* EEPROM connected, with the BIOS being responsible for
@@ -1085,6 +1088,7 @@ static void realtek_detect ( struct realtek_nic *rtl ) {
rtl );
rtl->legacy = 1;
}
+ rtl->eeprom.bus = &rtl->spibit.bus;
}
}
@@ -1132,7 +1136,8 @@ static int realtek_probe ( struct pci_device *pci ) {
realtek_detect ( rtl );
/* Initialise EEPROM */
- if ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) {
+ if ( rtl->eeprom.bus &&
+ ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) ) {
/* Read MAC address from EEPROM */
if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_MAC,
diff --git a/qemu/roms/ipxe/src/drivers/net/realtek.h b/qemu/roms/ipxe/src/drivers/net/realtek.h
index ac33405e8..b1ce7f98f 100644
--- a/qemu/roms/ipxe/src/drivers/net/realtek.h
+++ b/qemu/roms/ipxe/src/drivers/net/realtek.h
@@ -7,7 +7,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/spi.h>
#include <ipxe/spi_bit.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c
index 8851d1bfb..5f97480fa 100644
--- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c
+++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c
@@ -3,15 +3,23 @@
FILE_LICENCE(GPL2_OR_LATER);
#include <ipxe/pci.h>
+#include "rtl818x.h"
-REQUIRE_OBJECT(rtl818x);
-REQUIRE_OBJECT(rtl8180_grf5101);
-REQUIRE_OBJECT(rtl8180_max2820);
-REQUIRE_OBJECT(rtl8180_sa2400);
-
-static struct pci_device_id rtl8180_nics[] __unused = {
+static struct pci_device_id rtl8180_nics[] = {
PCI_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0),
PCI_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0),
PCI_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0),
PCI_ROM(0x1186, 0x3300, "dwl510", "D-Link DWL-510", 0),
};
+
+struct pci_driver rtl8180_driver __pci_driver = {
+ .ids = rtl8180_nics,
+ .id_count = sizeof(rtl8180_nics) / sizeof(rtl8180_nics[0]),
+ .probe = rtl818x_probe,
+ .remove = rtl818x_remove,
+};
+
+REQUIRING_SYMBOL(rtl8180_driver);
+REQUIRE_OBJECT(rtl8180_grf5101);
+REQUIRE_OBJECT(rtl8180_max2820);
+REQUIRE_OBJECT(rtl8180_sa2400);
diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c
index fd27e5c8c..234978cea 100644
--- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c
+++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c
@@ -3,12 +3,20 @@
FILE_LICENCE(GPL2_OR_LATER);
#include <ipxe/pci.h>
-
-REQUIRE_OBJECT(rtl818x);
-REQUIRE_OBJECT(rtl8185_rtl8225);
+#include "rtl818x.h"
static struct pci_device_id rtl8185_nics[] __unused = {
PCI_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0),
PCI_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0),
PCI_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0),
};
+
+struct pci_driver rtl8185_driver __pci_driver = {
+ .ids = rtl8185_nics,
+ .id_count = sizeof(rtl8185_nics) / sizeof(rtl8185_nics[0]),
+ .probe = rtl818x_probe,
+ .remove = rtl818x_remove,
+};
+
+REQUIRING_SYMBOL(rtl8185_driver);
+REQUIRE_OBJECT(rtl8185_rtl8225);
diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c
index cf4c7556f..8b3c206d4 100644
--- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c
+++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c
@@ -649,7 +649,7 @@ struct net80211_device_operations rtl818x_operations = {
.config = rtl818x_config,
};
-static int rtl818x_probe(struct pci_device *pdev )
+int rtl818x_probe(struct pci_device *pdev )
{
struct net80211_device *dev;
struct rtl818x_priv *priv;
@@ -820,7 +820,7 @@ static int rtl818x_probe(struct pci_device *pdev )
return err;
}
-static void rtl818x_remove(struct pci_device *pdev)
+void rtl818x_remove(struct pci_device *pdev)
{
struct net80211_device *dev = pci_get_drvdata(pdev);
@@ -830,25 +830,3 @@ static void rtl818x_remove(struct pci_device *pdev)
net80211_unregister(dev);
net80211_free(dev);
}
-
-/* Hide PCI_ROM definitions in here from parserom.pl; the definitions
- that should be used are in rtl8180.c and rtl8185.c. */
-#define RTL_ROM PCI_ROM
-
-static struct pci_device_id rtl818x_nics[] = {
- RTL_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0),
- RTL_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0),
- RTL_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0),
-
- RTL_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0),
- RTL_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0),
- RTL_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0),
- RTL_ROM(0x1186, 0x3300, "dwl510", "D-Link DWL-510", 0),
-};
-
-struct pci_driver rtl818x_driver __pci_driver = {
- .ids = rtl818x_nics,
- .id_count = sizeof(rtl818x_nics) / sizeof(rtl818x_nics[0]),
- .probe = rtl818x_probe,
- .remove = rtl818x_remove,
-};
diff --git a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h
index 4e57d0bd3..ae4b8a96f 100644
--- a/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h
+++ b/qemu/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h
@@ -19,6 +19,7 @@
#include <ipxe/spi_bit.h>
#include <ipxe/tables.h>
+#include <ipxe/net80211.h>
FILE_LICENCE(GPL2_ONLY);
@@ -356,4 +357,7 @@ struct rtl818x_rf_ops {
void (*conf_erp)(struct net80211_device *dev); /* set based on dev->erp_flags */
};
+extern int rtl818x_probe(struct pci_device *pdev );
+extern void rtl818x_remove(struct pci_device *pdev);
+
#endif /* RTL818X_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/skeleton.c b/qemu/roms/ipxe/src/drivers/net/skeleton.c
index 365111b8d..0435b9d0e 100644
--- a/qemu/roms/ipxe/src/drivers/net/skeleton.c
+++ b/qemu/roms/ipxe/src/drivers/net/skeleton.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/skeleton.h b/qemu/roms/ipxe/src/drivers/net/skeleton.h
index 3de2afa5b..2ab01bd56 100644
--- a/qemu/roms/ipxe/src/drivers/net/skeleton.h
+++ b/qemu/roms/ipxe/src/drivers/net/skeleton.h
@@ -7,7 +7,7 @@
*
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Skeleton BAR size */
#define SKELETON_BAR_SIZE 256
diff --git a/qemu/roms/ipxe/src/drivers/net/smsc75xx.c b/qemu/roms/ipxe/src/drivers/net/smsc75xx.c
new file mode 100644
index 000000000..017e02a59
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/smsc75xx.c
@@ -0,0 +1,1057 @@
+/*
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/usb.h>
+#include <ipxe/usbnet.h>
+#include <ipxe/profile.h>
+#include "smsc75xx.h"
+
+/** @file
+ *
+ * SMSC LAN75xx USB Ethernet driver
+ *
+ */
+
+/** Interrupt completion profiler */
+static struct profiler smsc75xx_intr_profiler __profiler =
+ { .name = "smsc75xx.intr" };
+
+/** Bulk IN completion profiler */
+static struct profiler smsc75xx_in_profiler __profiler =
+ { .name = "smsc75xx.in" };
+
+/** Bulk OUT profiler */
+static struct profiler smsc75xx_out_profiler __profiler =
+ { .name = "smsc75xx.out" };
+
+/******************************************************************************
+ *
+ * Register access
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Write register (without byte-swapping)
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v address Register address
+ * @v value Register value
+ * @ret rc Return status code
+ */
+static int smsc75xx_raw_writel ( struct smsc75xx_device *smsc75xx,
+ unsigned int address, uint32_t value ) {
+ int rc;
+
+ /* Write register */
+ if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_WRITE, 0,
+ address, &value, sizeof ( value ) ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not write %03x: %s\n",
+ smsc75xx, address, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Write register
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v address Register address
+ * @v value Register value
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+smsc75xx_writel ( struct smsc75xx_device *smsc75xx, unsigned int address,
+ uint32_t value ) {
+ int rc;
+
+ /* Write register */
+ if ( ( rc = smsc75xx_raw_writel ( smsc75xx, address,
+ cpu_to_le32 ( value ) ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Read register (without byte-swapping)
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v address Register address
+ * @ret value Register value
+ * @ret rc Return status code
+ */
+static int smsc75xx_raw_readl ( struct smsc75xx_device *smsc75xx,
+ unsigned int address, uint32_t *value ) {
+ int rc;
+
+ /* Read register */
+ if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_READ, 0,
+ address, value, sizeof ( *value ) ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not read %03x: %s\n",
+ smsc75xx, address, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Read register
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v address Register address
+ * @ret value Register value
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+smsc75xx_readl ( struct smsc75xx_device *smsc75xx, unsigned int address,
+ uint32_t *value ) {
+ int rc;
+
+ /* Read register */
+ if ( ( rc = smsc75xx_raw_readl ( smsc75xx, address, value ) ) != 0 )
+ return rc;
+ le32_to_cpus ( value );
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * EEPROM access
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Wait for EEPROM to become idle
+ *
+ * @v smsc75xx SMSC75xx device
+ * @ret rc Return status code
+ */
+static int smsc75xx_eeprom_wait ( struct smsc75xx_device *smsc75xx ) {
+ uint32_t e2p_cmd;
+ unsigned int i;
+ int rc;
+
+ /* Wait for EPC_BSY to become clear */
+ for ( i = 0 ; i < SMSC75XX_EEPROM_MAX_WAIT_MS ; i++ ) {
+
+ /* Read E2P_CMD and check EPC_BSY */
+ if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_CMD,
+ &e2p_cmd ) ) != 0 )
+ return rc;
+ if ( ! ( e2p_cmd & SMSC75XX_E2P_CMD_EPC_BSY ) )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for EEPROM\n",
+ smsc75xx );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Read byte from EEPROM
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v address EEPROM address
+ * @ret byte Byte read, or negative error
+ */
+static int smsc75xx_eeprom_read_byte ( struct smsc75xx_device *smsc75xx,
+ unsigned int address ) {
+ uint32_t e2p_cmd;
+ uint32_t e2p_data;
+ int rc;
+
+ /* Wait for EEPROM to become idle */
+ if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 )
+ return rc;
+
+ /* Initiate read command */
+ e2p_cmd = ( SMSC75XX_E2P_CMD_EPC_BSY | SMSC75XX_E2P_CMD_EPC_CMD_READ |
+ SMSC75XX_E2P_CMD_EPC_ADDR ( address ) );
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_E2P_CMD,
+ e2p_cmd ) ) != 0 )
+ return rc;
+
+ /* Wait for command to complete */
+ if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 )
+ return rc;
+
+ /* Read EEPROM data */
+ if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_DATA,
+ &e2p_data ) ) != 0 )
+ return rc;
+
+ return SMSC75XX_E2P_DATA_GET ( e2p_data );
+}
+
+/**
+ * Read data from EEPROM
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v address EEPROM address
+ * @v data Data buffer
+ * @v len Length of data
+ * @ret rc Return status code
+ */
+static int smsc75xx_eeprom_read ( struct smsc75xx_device *smsc75xx,
+ unsigned int address, void *data,
+ size_t len ) {
+ uint8_t *bytes;
+ int byte;
+
+ /* Read bytes */
+ for ( bytes = data ; len-- ; address++, bytes++ ) {
+ byte = smsc75xx_eeprom_read_byte ( smsc75xx, address );
+ if ( byte < 0 )
+ return byte;
+ *bytes = byte;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * MII access
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Wait for MII to become idle
+ *
+ * @v smsc75xx SMSC75xx device
+ * @ret rc Return status code
+ */
+static int smsc75xx_mii_wait ( struct smsc75xx_device *smsc75xx ) {
+ uint32_t mii_access;
+ unsigned int i;
+ int rc;
+
+ /* Wait for MIIBZY to become clear */
+ for ( i = 0 ; i < SMSC75XX_MII_MAX_WAIT_MS ; i++ ) {
+
+ /* Read MII_ACCESS and check MIIBZY */
+ if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_ACCESS,
+ &mii_access ) ) != 0 )
+ return rc;
+ if ( ! ( mii_access & SMSC75XX_MII_ACCESS_MIIBZY ) )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for MII\n",
+ smsc75xx );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Read from MII register
+ *
+ * @v mii MII interface
+ * @v reg Register address
+ * @ret value Data read, or negative error
+ */
+static int smsc75xx_mii_read ( struct mii_interface *mii, unsigned int reg ) {
+ struct smsc75xx_device *smsc75xx =
+ container_of ( mii, struct smsc75xx_device, mii );
+ uint32_t mii_access;
+ uint32_t mii_data;
+ int rc;
+
+ /* Wait for MII to become idle */
+ if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 )
+ return rc;
+
+ /* Initiate read command */
+ mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS |
+ SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) |
+ SMSC75XX_MII_ACCESS_MIIBZY );
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS,
+ mii_access ) ) != 0 )
+ return rc;
+
+ /* Wait for command to complete */
+ if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 )
+ return rc;
+
+ /* Read MII data */
+ if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_DATA,
+ &mii_data ) ) != 0 )
+ return rc;
+
+ return SMSC75XX_MII_DATA_GET ( mii_data );
+}
+
+/**
+ * Write to MII register
+ *
+ * @v mii MII interface
+ * @v reg Register address
+ * @v data Data to write
+ * @ret rc Return status code
+ */
+static int smsc75xx_mii_write ( struct mii_interface *mii, unsigned int reg,
+ unsigned int data ) {
+ struct smsc75xx_device *smsc75xx =
+ container_of ( mii, struct smsc75xx_device, mii );
+ uint32_t mii_access;
+ uint32_t mii_data;
+ int rc;
+
+ /* Wait for MII to become idle */
+ if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 )
+ return rc;
+
+ /* Write MII data */
+ mii_data = SMSC75XX_MII_DATA_SET ( data );
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_DATA,
+ mii_data ) ) != 0 )
+ return rc;
+
+ /* Initiate write command */
+ mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS |
+ SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) |
+ SMSC75XX_MII_ACCESS_MIIWNR |
+ SMSC75XX_MII_ACCESS_MIIBZY );
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS,
+ mii_access ) ) != 0 )
+ return rc;
+
+ /* Wait for command to complete */
+ if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/** MII operations */
+static struct mii_operations smsc75xx_mii_operations = {
+ .read = smsc75xx_mii_read,
+ .write = smsc75xx_mii_write,
+};
+
+/**
+ * Check link status
+ *
+ * @v smsc75xx SMSC75xx device
+ * @ret rc Return status code
+ */
+static int smsc75xx_check_link ( struct smsc75xx_device *smsc75xx ) {
+ struct net_device *netdev = smsc75xx->netdev;
+ int intr;
+ int rc;
+
+ /* Read PHY interrupt source */
+ intr = mii_read ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE );
+ if ( intr < 0 ) {
+ rc = intr;
+ DBGC ( smsc75xx, "SMSC75XX %p could not get PHY interrupt "
+ "source: %s\n", smsc75xx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Acknowledge PHY interrupt */
+ if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE,
+ intr ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not acknowledge PHY "
+ "interrupt: %s\n", smsc75xx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check link status */
+ if ( ( rc = mii_check_link ( &smsc75xx->mii, netdev ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not check link: %s\n",
+ smsc75xx, strerror ( rc ) );
+ return rc;
+ }
+
+ DBGC ( smsc75xx, "SMSC75XX %p link %s (intr %#04x)\n",
+ smsc75xx, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr );
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Statistics (for debugging)
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Get statistics
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v stats Statistics to fill in
+ * @ret rc Return status code
+ */
+static int smsc75xx_get_statistics ( struct smsc75xx_device *smsc75xx,
+ struct smsc75xx_statistics *stats ) {
+ int rc;
+
+ /* Get statistics */
+ if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_GET_STATISTICS, 0, 0,
+ stats, sizeof ( *stats ) ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not get statistics: %s\n",
+ smsc75xx, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Dump statistics (for debugging)
+ *
+ * @v smsc75xx SMSC75xx device
+ * @ret rc Return status code
+ */
+static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) {
+ struct smsc75xx_statistics stats;
+ int rc;
+
+ /* Do nothing unless debugging is enabled */
+ if ( ! DBG_LOG )
+ return 0;
+
+ /* Get statistics */
+ if ( ( rc = smsc75xx_get_statistics ( smsc75xx, &stats ) ) != 0 )
+ return rc;
+
+ /* Dump statistics */
+ DBGC ( smsc75xx, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d "
+ "ovr %d drp %d\n", smsc75xx, le32_to_cpu ( stats.rx.err.fcs ),
+ le32_to_cpu ( stats.rx.err.alignment ),
+ le32_to_cpu ( stats.rx.err.fragment ),
+ le32_to_cpu ( stats.rx.err.jabber ),
+ le32_to_cpu ( stats.rx.err.undersize ),
+ le32_to_cpu ( stats.rx.err.oversize ),
+ le32_to_cpu ( stats.rx.err.dropped ) );
+ DBGC ( smsc75xx, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n",
+ smsc75xx, le32_to_cpu ( stats.rx.byte.unicast ),
+ le32_to_cpu ( stats.rx.byte.broadcast ),
+ le32_to_cpu ( stats.rx.byte.multicast ) );
+ DBGC ( smsc75xx, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause "
+ "%d\n", smsc75xx, le32_to_cpu ( stats.rx.frame.unicast ),
+ le32_to_cpu ( stats.rx.frame.broadcast ),
+ le32_to_cpu ( stats.rx.frame.multicast ),
+ le32_to_cpu ( stats.rx.frame.pause ) );
+ DBGC ( smsc75xx, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d "
+ "mul %d exc %d lat %d\n", smsc75xx,
+ le32_to_cpu ( stats.tx.err.fcs ),
+ le32_to_cpu ( stats.tx.err.deferral ),
+ le32_to_cpu ( stats.tx.err.carrier ),
+ le32_to_cpu ( stats.tx.err.count ),
+ le32_to_cpu ( stats.tx.err.single ),
+ le32_to_cpu ( stats.tx.err.multiple ),
+ le32_to_cpu ( stats.tx.err.excessive ),
+ le32_to_cpu ( stats.tx.err.late ) );
+ DBGC ( smsc75xx, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n",
+ smsc75xx, le32_to_cpu ( stats.tx.byte.unicast ),
+ le32_to_cpu ( stats.tx.byte.broadcast ),
+ le32_to_cpu ( stats.tx.byte.multicast ) );
+ DBGC ( smsc75xx, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause "
+ "%d\n", smsc75xx, le32_to_cpu ( stats.tx.frame.unicast ),
+ le32_to_cpu ( stats.tx.frame.broadcast ),
+ le32_to_cpu ( stats.tx.frame.multicast ),
+ le32_to_cpu ( stats.tx.frame.pause ) );
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Device reset
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Reset device
+ *
+ * @v smsc75xx SMSC75xx device
+ * @ret rc Return status code
+ */
+static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) {
+ uint32_t hw_cfg;
+ int rc;
+
+ /* Reset device */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG,
+ SMSC75XX_HW_CFG_LRST ) ) != 0 )
+ return rc;
+
+ /* Wait for reset to complete */
+ udelay ( SMSC75XX_RESET_DELAY_US );
+
+ /* Check that reset has completed */
+ if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_HW_CFG,
+ &hw_cfg ) ) != 0 )
+ return rc;
+ if ( hw_cfg & SMSC75XX_HW_CFG_LRST ) {
+ DBGC ( smsc75xx, "SMSC75XX %p failed to reset\n", smsc75xx );
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Endpoint operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Complete interrupt transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void smsc75xx_intr_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct smsc75xx_device *smsc75xx =
+ container_of ( ep, struct smsc75xx_device, usbnet.intr );
+ struct net_device *netdev = smsc75xx->netdev;
+ struct smsc75xx_interrupt *intr;
+
+ /* Profile completions */
+ profile_start ( &smsc75xx_intr_profiler );
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open )
+ goto done;
+
+ /* Record USB errors against the network device */
+ if ( rc != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p interrupt failed: %s\n",
+ smsc75xx, strerror ( rc ) );
+ DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) );
+ netdev_rx_err ( netdev, NULL, rc );
+ goto done;
+ }
+
+ /* Extract interrupt data */
+ if ( iob_len ( iobuf ) != sizeof ( *intr ) ) {
+ DBGC ( smsc75xx, "SMSC75XX %p malformed interrupt\n",
+ smsc75xx );
+ DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) );
+ netdev_rx_err ( netdev, NULL, rc );
+ goto done;
+ }
+ intr = iobuf->data;
+
+ /* Record interrupt status */
+ smsc75xx->int_sts = le32_to_cpu ( intr->int_sts );
+ profile_stop ( &smsc75xx_intr_profiler );
+
+ done:
+ /* Free I/O buffer */
+ free_iob ( iobuf );
+}
+
+/** Interrupt endpoint operations */
+static struct usb_endpoint_driver_operations smsc75xx_intr_operations = {
+ .complete = smsc75xx_intr_complete,
+};
+
+/**
+ * Complete bulk IN transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void smsc75xx_in_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct smsc75xx_device *smsc75xx =
+ container_of ( ep, struct smsc75xx_device, usbnet.in );
+ struct net_device *netdev = smsc75xx->netdev;
+ struct smsc75xx_rx_header *header;
+
+ /* Profile completions */
+ profile_start ( &smsc75xx_in_profiler );
+
+ /* Ignore packets cancelled when the endpoint closes */
+ if ( ! ep->open ) {
+ free_iob ( iobuf );
+ return;
+ }
+
+ /* Record USB errors against the network device */
+ if ( rc != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p bulk IN failed: %s\n",
+ smsc75xx, strerror ( rc ) );
+ goto err;
+ }
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < ( sizeof ( *header ) ) ) {
+ DBGC ( smsc75xx, "SMSC75XX %p underlength bulk IN\n",
+ smsc75xx );
+ DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* Strip header */
+ header = iobuf->data;
+ iob_pull ( iobuf, sizeof ( *header ) );
+
+ /* Check for errors */
+ if ( header->command & cpu_to_le32 ( SMSC75XX_RX_RED ) ) {
+ DBGC ( smsc75xx, "SMSC75XX %p receive error (%08x):\n",
+ smsc75xx, le32_to_cpu ( header->command ) );
+ DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -EIO;
+ goto err;
+ }
+
+ /* Hand off to network stack */
+ netdev_rx ( netdev, iob_disown ( iobuf ) );
+
+ profile_stop ( &smsc75xx_in_profiler );
+ return;
+
+ err:
+ /* Hand off to network stack */
+ netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
+}
+
+/** Bulk IN endpoint operations */
+static struct usb_endpoint_driver_operations smsc75xx_in_operations = {
+ .complete = smsc75xx_in_complete,
+};
+
+/**
+ * Transmit packet
+ *
+ * @v smsc75xx SMSC75xx device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int smsc75xx_out_transmit ( struct smsc75xx_device *smsc75xx,
+ struct io_buffer *iobuf ) {
+ struct smsc75xx_tx_header *header;
+ size_t len = iob_len ( iobuf );
+ int rc;
+
+ /* Profile transmissions */
+ profile_start ( &smsc75xx_out_profiler );
+
+ /* Prepend header */
+ if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 )
+ return rc;
+ header = iob_push ( iobuf, sizeof ( *header ) );
+ header->command = cpu_to_le32 ( SMSC75XX_TX_FCS | len );
+ header->tag = 0;
+ header->mss = 0;
+
+ /* Enqueue I/O buffer */
+ if ( ( rc = usb_stream ( &smsc75xx->usbnet.out, iobuf, 0 ) ) != 0 )
+ return rc;
+
+ profile_stop ( &smsc75xx_out_profiler );
+ return 0;
+}
+
+/**
+ * Complete bulk OUT transfer
+ *
+ * @v ep USB endpoint
+ * @v iobuf I/O buffer
+ * @v rc Completion status code
+ */
+static void smsc75xx_out_complete ( struct usb_endpoint *ep,
+ struct io_buffer *iobuf, int rc ) {
+ struct smsc75xx_device *smsc75xx =
+ container_of ( ep, struct smsc75xx_device, usbnet.out );
+ struct net_device *netdev = smsc75xx->netdev;
+
+ /* Report TX completion */
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+}
+
+/** Bulk OUT endpoint operations */
+static struct usb_endpoint_driver_operations smsc75xx_out_operations = {
+ .complete = smsc75xx_out_complete,
+};
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int smsc75xx_open ( struct net_device *netdev ) {
+ struct smsc75xx_device *smsc75xx = netdev->priv;
+ union smsc75xx_mac mac;
+ int rc;
+
+ /* Clear stored interrupt status */
+ smsc75xx->int_sts = 0;
+
+ /* Copy MAC address */
+ memset ( &mac, 0, sizeof ( mac ) );
+ memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN );
+
+ /* Configure bulk IN empty response */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG,
+ SMSC75XX_HW_CFG_BIR ) ) != 0 )
+ goto err_hw_cfg;
+
+ /* Open USB network device */
+ if ( ( rc = usbnet_open ( &smsc75xx->usbnet ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not open: %s\n",
+ smsc75xx, strerror ( rc ) );
+ goto err_open;
+ }
+
+ /* Configure interrupt endpoint */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_EP_CTL,
+ ( SMSC75XX_INT_EP_CTL_RDFO_EN |
+ SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 )
+ goto err_int_ep_ctl;
+
+ /* Configure bulk IN delay */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_BULK_IN_DLY,
+ SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 )
+ goto err_bulk_in_dly;
+
+ /* Configure receive filters */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_RFE_CTL,
+ ( SMSC75XX_RFE_CTL_AB |
+ SMSC75XX_RFE_CTL_AM |
+ SMSC75XX_RFE_CTL_AU ) ) ) != 0 )
+ goto err_rfe_ctl;
+
+ /* Configure receive FIFO */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_RX_CTL,
+ ( SMSC75XX_FCT_RX_CTL_EN |
+ SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 )
+ goto err_fct_rx_ctl;
+
+ /* Configure transmit FIFO */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_TX_CTL,
+ SMSC75XX_FCT_TX_CTL_EN ) ) != 0 )
+ goto err_fct_tx_ctl;
+
+ /* Configure receive datapath */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_RX,
+ ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT |
+ SMSC75XX_MAC_RX_FCS |
+ SMSC75XX_MAC_RX_EN ) ) ) != 0 )
+ goto err_mac_rx;
+
+ /* Configure transmit datapath */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_TX,
+ SMSC75XX_MAC_TX_EN ) ) != 0 )
+ goto err_mac_tx;
+
+ /* Write MAC address high register */
+ if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRH,
+ mac.addr.h ) ) != 0 )
+ goto err_rx_addrh;
+
+ /* Write MAC address low register */
+ if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRL,
+ mac.addr.l ) ) != 0 )
+ goto err_rx_addrl;
+
+ /* Write MAC address perfect filter high register */
+ mac.addr.h |= cpu_to_le32 ( SMSC75XX_ADDR_FILTH_VALID );
+ if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTH ( 0 ),
+ mac.addr.h ) ) != 0 )
+ goto err_addr_filth;
+
+ /* Write MAC address perfect filter low register */
+ if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTL ( 0 ),
+ mac.addr.l ) ) != 0 )
+ goto err_addr_filtl;
+
+ /* Enable PHY interrupts */
+ if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_MASK,
+ ( SMSC75XX_PHY_INTR_ANEG_DONE |
+ SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not set PHY interrupt "
+ "mask: %s\n", smsc75xx, strerror ( rc ) );
+ goto err_phy_intr_mask;
+ }
+
+ /* Update link status */
+ smsc75xx_check_link ( smsc75xx );
+
+ return 0;
+
+ err_phy_intr_mask:
+ err_addr_filtl:
+ err_addr_filth:
+ err_rx_addrl:
+ err_rx_addrh:
+ err_mac_tx:
+ err_mac_rx:
+ err_fct_tx_ctl:
+ err_fct_rx_ctl:
+ err_rfe_ctl:
+ err_bulk_in_dly:
+ err_int_ep_ctl:
+ usbnet_close ( &smsc75xx->usbnet );
+ err_open:
+ err_hw_cfg:
+ smsc75xx_reset ( smsc75xx );
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void smsc75xx_close ( struct net_device *netdev ) {
+ struct smsc75xx_device *smsc75xx = netdev->priv;
+
+ /* Close USB network device */
+ usbnet_close ( &smsc75xx->usbnet );
+
+ /* Dump statistics (for debugging) */
+ smsc75xx_dump_statistics ( smsc75xx );
+
+ /* Reset device */
+ smsc75xx_reset ( smsc75xx );
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int smsc75xx_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct smsc75xx_device *smsc75xx = netdev->priv;
+ int rc;
+
+ /* Transmit packet */
+ if ( ( rc = smsc75xx_out_transmit ( smsc75xx, iobuf ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v netdev Network device
+ */
+static void smsc75xx_poll ( struct net_device *netdev ) {
+ struct smsc75xx_device *smsc75xx = netdev->priv;
+ uint32_t int_sts;
+ int rc;
+
+ /* Poll USB bus */
+ usb_poll ( smsc75xx->bus );
+
+ /* Refill endpoints */
+ if ( ( rc = usbnet_refill ( &smsc75xx->usbnet ) ) != 0 )
+ netdev_rx_err ( netdev, NULL, rc );
+
+ /* Do nothing more unless there are interrupts to handle */
+ int_sts = smsc75xx->int_sts;
+ if ( ! int_sts )
+ return;
+
+ /* Check link status if applicable */
+ if ( int_sts & SMSC75XX_INT_STS_PHY_INT ) {
+ smsc75xx_check_link ( smsc75xx );
+ int_sts &= ~SMSC75XX_INT_STS_PHY_INT;
+ }
+
+ /* Record RX FIFO overflow if applicable */
+ if ( int_sts & SMSC75XX_INT_STS_RDFO_INT ) {
+ DBGC2 ( smsc75xx, "SMSC75XX %p RX FIFO overflowed\n",
+ smsc75xx );
+ netdev_rx_err ( netdev, NULL, -ENOBUFS );
+ int_sts &= ~SMSC75XX_INT_STS_RDFO_INT;
+ }
+
+ /* Check for unexpected interrupts */
+ if ( int_sts ) {
+ DBGC ( smsc75xx, "SMSC75XX %p unexpected interrupt %#08x\n",
+ smsc75xx, int_sts );
+ netdev_rx_err ( netdev, NULL, -ENOTTY );
+ }
+
+ /* Clear interrupts */
+ if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_STS,
+ smsc75xx->int_sts ) ) != 0 )
+ netdev_rx_err ( netdev, NULL, rc );
+ smsc75xx->int_sts = 0;
+}
+
+/** SMSC75xx network device operations */
+static struct net_device_operations smsc75xx_operations = {
+ .open = smsc75xx_open,
+ .close = smsc75xx_close,
+ .transmit = smsc75xx_transmit,
+ .poll = smsc75xx_poll,
+};
+
+/******************************************************************************
+ *
+ * USB interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe device
+ *
+ * @v func USB function
+ * @v config Configuration descriptor
+ * @ret rc Return status code
+ */
+static int smsc75xx_probe ( struct usb_function *func,
+ struct usb_configuration_descriptor *config ) {
+ struct usb_device *usb = func->usb;
+ struct net_device *netdev;
+ struct smsc75xx_device *smsc75xx;
+ int rc;
+
+ /* Allocate and initialise structure */
+ netdev = alloc_etherdev ( sizeof ( *smsc75xx ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &smsc75xx_operations );
+ netdev->dev = &func->dev;
+ smsc75xx = netdev->priv;
+ memset ( smsc75xx, 0, sizeof ( *smsc75xx ) );
+ smsc75xx->usb = usb;
+ smsc75xx->bus = usb->port->hub->bus;
+ smsc75xx->netdev = netdev;
+ usbnet_init ( &smsc75xx->usbnet, func, &smsc75xx_intr_operations,
+ &smsc75xx_in_operations, &smsc75xx_out_operations );
+ usb_refill_init ( &smsc75xx->usbnet.intr, 0, SMSC75XX_INTR_MAX_FILL );
+ usb_refill_init ( &smsc75xx->usbnet.in, SMSC75XX_IN_MTU,
+ SMSC75XX_IN_MAX_FILL );
+ mii_init ( &smsc75xx->mii, &smsc75xx_mii_operations );
+ DBGC ( smsc75xx, "SMSC75XX %p on %s\n", smsc75xx, func->name );
+
+ /* Describe USB network device */
+ if ( ( rc = usbnet_describe ( &smsc75xx->usbnet, config ) ) != 0 ) {
+ DBGC ( smsc75xx, "SMSC75XX %p could not describe: %s\n",
+ smsc75xx, strerror ( rc ) );
+ goto err_describe;
+ }
+
+ /* Reset device */
+ if ( ( rc = smsc75xx_reset ( smsc75xx ) ) != 0 )
+ goto err_reset;
+
+ /* Read MAC address */
+ if ( ( rc = smsc75xx_eeprom_read ( smsc75xx, SMSC75XX_EEPROM_MAC,
+ netdev->hw_addr, ETH_ALEN ) ) != 0 )
+ goto err_eeprom_read;
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+
+ usb_func_set_drvdata ( func, netdev );
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register:
+ err_eeprom_read:
+ err_reset:
+ err_describe:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove device
+ *
+ * @v func USB function
+ */
+static void smsc75xx_remove ( struct usb_function *func ) {
+ struct net_device *netdev = usb_func_get_drvdata ( func );
+
+ unregister_netdev ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** SMSC75xx device IDs */
+static struct usb_device_id smsc75xx_ids[] = {
+ {
+ .name = "smsc7500",
+ .vendor = 0x0424,
+ .product = 0x7500,
+ .class = { 0xff, 0x00, 0xff },
+ },
+ {
+ .name = "smsc7505",
+ .vendor = 0x0424,
+ .product = 0x7505,
+ .class = { 0xff, 0x00, 0xff },
+ },
+};
+
+/** SMSC LAN75xx driver */
+struct usb_driver smsc75xx_driver __usb_driver = {
+ .ids = smsc75xx_ids,
+ .id_count = ( sizeof ( smsc75xx_ids ) / sizeof ( smsc75xx_ids[0] ) ),
+ .probe = smsc75xx_probe,
+ .remove = smsc75xx_remove,
+};
diff --git a/qemu/roms/ipxe/src/drivers/net/smsc75xx.h b/qemu/roms/ipxe/src/drivers/net/smsc75xx.h
new file mode 100644
index 000000000..2463b72a1
--- /dev/null
+++ b/qemu/roms/ipxe/src/drivers/net/smsc75xx.h
@@ -0,0 +1,309 @@
+#ifndef _SMSC75XX_H
+#define _SMSC75XX_H
+
+/** @file
+ *
+ * SMSC LAN75xx USB Ethernet driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/usb.h>
+#include <ipxe/usbnet.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/mii.h>
+
+/** Register write command */
+#define SMSC75XX_REGISTER_WRITE \
+ ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \
+ USB_REQUEST_TYPE ( 0xa0 ) )
+
+/** Register read command */
+#define SMSC75XX_REGISTER_READ \
+ ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \
+ USB_REQUEST_TYPE ( 0xa1 ) )
+
+/** Get statistics command */
+#define SMSC75XX_GET_STATISTICS \
+ ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \
+ USB_REQUEST_TYPE ( 0xa2 ) )
+
+/** Interrupt status register */
+#define SMSC75XX_INT_STS 0x00c
+#define SMSC75XX_INT_STS_RDFO_INT 0x00400000UL /**< RX FIFO overflow */
+#define SMSC75XX_INT_STS_PHY_INT 0x00020000UL /**< PHY interrupt */
+
+/** Hardware configuration register */
+#define SMSC75XX_HW_CFG 0x010
+#define SMSC75XX_HW_CFG_BIR 0x00000080UL /**< Bulk IN use NAK */
+#define SMSC75XX_HW_CFG_LRST 0x00000002UL /**< Soft lite reset */
+
+/** Interrupt endpoint control register */
+#define SMSC75XX_INT_EP_CTL 0x038
+#define SMSC75XX_INT_EP_CTL_RDFO_EN 0x00400000UL /**< RX FIFO overflow */
+#define SMSC75XX_INT_EP_CTL_PHY_EN 0x00020000UL /**< PHY interrupt */
+
+/** Bulk IN delay register */
+#define SMSC75XX_BULK_IN_DLY 0x03c
+#define SMSC75XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */
+
+/** EEPROM command register */
+#define SMSC75XX_E2P_CMD 0x040
+#define SMSC75XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */
+#define SMSC75XX_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */
+#define SMSC75XX_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */
+
+/** EEPROM data register */
+#define SMSC75XX_E2P_DATA 0x044
+#define SMSC75XX_E2P_DATA_GET(e2p_data) \
+ ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */
+
+/** MAC address EEPROM address */
+#define SMSC75XX_EEPROM_MAC 0x01
+
+/** Receive filtering engine control register */
+#define SMSC75XX_RFE_CTL 0x060
+#define SMSC75XX_RFE_CTL_AB 0x00000400UL /**< Accept broadcast */
+#define SMSC75XX_RFE_CTL_AM 0x00000200UL /**< Accept multicast */
+#define SMSC75XX_RFE_CTL_AU 0x00000100UL /**< Accept unicast */
+
+/** FIFO controller RX FIFO control register */
+#define SMSC75XX_FCT_RX_CTL 0x090
+#define SMSC75XX_FCT_RX_CTL_EN 0x80000000UL /**< FCT RX enable */
+#define SMSC75XX_FCT_RX_CTL_BAD 0x02000000UL /**< Store bad frames */
+
+/** FIFO controller TX FIFO control register */
+#define SMSC75XX_FCT_TX_CTL 0x094
+#define SMSC75XX_FCT_TX_CTL_EN 0x80000000UL /**< FCT TX enable */
+
+/** MAC receive register */
+#define SMSC75XX_MAC_RX 0x104
+#define SMSC75XX_MAC_RX_MAX_SIZE(mtu) ( (mtu) << 16 ) /**< Max frame size */
+#define SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT \
+ SMSC75XX_MAC_RX_MAX_SIZE ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ )
+#define SMSC75XX_MAC_RX_FCS 0x00000010UL /**< FCS stripping */
+#define SMSC75XX_MAC_RX_EN 0x00000001UL /**< RX enable */
+
+/** MAC transmit register */
+#define SMSC75XX_MAC_TX 0x108
+#define SMSC75XX_MAC_TX_EN 0x00000001UL /**< TX enable */
+
+/** MAC receive address high register */
+#define SMSC75XX_RX_ADDRH 0x118
+
+/** MAC receive address low register */
+#define SMSC75XX_RX_ADDRL 0x11c
+
+/** MII access register */
+#define SMSC75XX_MII_ACCESS 0x120
+#define SMSC75XX_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */
+#define SMSC75XX_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */
+#define SMSC75XX_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */
+#define SMSC75XX_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */
+
+/** MII data register */
+#define SMSC75XX_MII_DATA 0x124
+#define SMSC75XX_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */
+#define SMSC75XX_MII_DATA_GET(mii_data) \
+ ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */
+
+/** PHY interrupt source MII register */
+#define SMSC75XX_MII_PHY_INTR_SOURCE 29
+
+/** PHY interrupt mask MII register */
+#define SMSC75XX_MII_PHY_INTR_MASK 30
+
+/** PHY interrupt: auto-negotiation complete */
+#define SMSC75XX_PHY_INTR_ANEG_DONE 0x0040
+
+/** PHY interrupt: link down */
+#define SMSC75XX_PHY_INTR_LINK_DOWN 0x0010
+
+/** MAC address perfect filter N high register */
+#define SMSC75XX_ADDR_FILTH(n) ( 0x300 + ( 8 * (n) ) )
+#define SMSC75XX_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */
+
+/** MAC address perfect filter N low register */
+#define SMSC75XX_ADDR_FILTL(n) ( 0x304 + ( 8 * (n) ) )
+
+/** MAC address */
+union smsc75xx_mac {
+ /** MAC receive address registers */
+ struct {
+ /** MAC receive address low register */
+ uint32_t l;
+ /** MAC receive address high register */
+ uint32_t h;
+ } __attribute__ (( packed )) addr;
+ /** Raw MAC address */
+ uint8_t raw[ETH_ALEN];
+};
+
+/** Receive packet header */
+struct smsc75xx_rx_header {
+ /** RX command word */
+ uint32_t command;
+ /** VLAN tag */
+ uint16_t vtag;
+ /** Checksum */
+ uint16_t csum;
+ /** Two-byte padding used to align Ethernet payload */
+ uint16_t pad;
+} __attribute__ (( packed ));
+
+/** Receive error detected */
+#define SMSC75XX_RX_RED 0x00400000UL
+
+/** Transmit packet header */
+struct smsc75xx_tx_header {
+ /** TX command word */
+ uint32_t command;
+ /** VLAN tag */
+ uint16_t tag;
+ /** Maximum segment size */
+ uint16_t mss;
+} __attribute__ (( packed ));
+
+/** Insert frame checksum and pad */
+#define SMSC75XX_TX_FCS 0x00400000UL
+
+/** Interrupt packet format */
+struct smsc75xx_interrupt {
+ /** Current value of INT_STS register */
+ uint32_t int_sts;
+} __attribute__ (( packed ));
+
+/** Byte count statistics */
+struct smsc75xx_byte_statistics {
+ /** Unicast byte count */
+ uint32_t unicast;
+ /** Broadcast byte count */
+ uint32_t broadcast;
+ /** Multicast byte count */
+ uint32_t multicast;
+} __attribute__ (( packed ));
+
+/** Frame count statistics */
+struct smsc75xx_frame_statistics {
+ /** Unicast frames */
+ uint32_t unicast;
+ /** Broadcast frames */
+ uint32_t broadcast;
+ /** Multicast frames */
+ uint32_t multicast;
+ /** Pause frames */
+ uint32_t pause;
+ /** Frames by length category */
+ uint32_t len[7];
+} __attribute__ (( packed ));
+
+/** Receive error statistics */
+struct smsc75xx_rx_error_statistics {
+ /** FCS errors */
+ uint32_t fcs;
+ /** Alignment errors */
+ uint32_t alignment;
+ /** Fragment errors */
+ uint32_t fragment;
+ /** Jabber errors */
+ uint32_t jabber;
+ /** Undersize frame errors */
+ uint32_t undersize;
+ /** Oversize frame errors */
+ uint32_t oversize;
+ /** Dropped frame errors */
+ uint32_t dropped;
+} __attribute__ (( packed ));
+
+/** Receive statistics */
+struct smsc75xx_rx_statistics {
+ /** Error statistics */
+ struct smsc75xx_rx_error_statistics err;
+ /** Byte count statistics */
+ struct smsc75xx_byte_statistics byte;
+ /** Frame count statistics */
+ struct smsc75xx_frame_statistics frame;
+} __attribute__ (( packed ));
+
+/** Transmit error statistics */
+struct smsc75xx_tx_error_statistics {
+ /** FCS errors */
+ uint32_t fcs;
+ /** Excess deferral errors */
+ uint32_t deferral;
+ /** Carrier errors */
+ uint32_t carrier;
+ /** Bad byte count */
+ uint32_t count;
+ /** Single collisions */
+ uint32_t single;
+ /** Multiple collisions */
+ uint32_t multiple;
+ /** Excession collisions */
+ uint32_t excessive;
+ /** Late collisions */
+ uint32_t late;
+} __attribute__ (( packed ));
+
+/** Transmit statistics */
+struct smsc75xx_tx_statistics {
+ /** Error statistics */
+ struct smsc75xx_tx_error_statistics err;
+ /** Byte count statistics */
+ struct smsc75xx_byte_statistics byte;
+ /** Frame count statistics */
+ struct smsc75xx_frame_statistics frame;
+} __attribute__ (( packed ));
+
+/** Statistics */
+struct smsc75xx_statistics {
+ /** Receive statistics */
+ struct smsc75xx_rx_statistics rx;
+ /** Transmit statistics */
+ struct smsc75xx_tx_statistics tx;
+} __attribute__ (( packed ));
+
+/** A SMSC75xx network device */
+struct smsc75xx_device {
+ /** USB device */
+ struct usb_device *usb;
+ /** USB bus */
+ struct usb_bus *bus;
+ /** Network device */
+ struct net_device *netdev;
+ /** USB network device */
+ struct usbnet_device usbnet;
+ /** MII interface */
+ struct mii_interface mii;
+ /** Interrupt status */
+ uint32_t int_sts;
+};
+
+/** Reset delay (in microseconds) */
+#define SMSC75XX_RESET_DELAY_US 2
+
+/** Maximum time to wait for EEPROM (in milliseconds) */
+#define SMSC75XX_EEPROM_MAX_WAIT_MS 100
+
+/** Maximum time to wait for MII (in milliseconds) */
+#define SMSC75XX_MII_MAX_WAIT_MS 100
+
+/** Interrupt maximum fill level
+ *
+ * This is a policy decision.
+ */
+#define SMSC75XX_INTR_MAX_FILL 2
+
+/** Bulk IN maximum fill level
+ *
+ * This is a policy decision.
+ */
+#define SMSC75XX_IN_MAX_FILL 8
+
+/** Bulk IN buffer size */
+#define SMSC75XX_IN_MTU \
+ ( sizeof ( struct smsc75xx_rx_header ) + \
+ ETH_FRAME_LEN + 4 /* possible VLAN header */ )
+
+#endif /* _SMSC75XX_H */
diff --git a/qemu/roms/ipxe/src/drivers/net/sundance.c b/qemu/roms/ipxe/src/drivers/net/sundance.c
index eef7c9c7c..9127fa2cd 100644
--- a/qemu/roms/ipxe/src/drivers/net/sundance.c
+++ b/qemu/roms/ipxe/src/drivers/net/sundance.c
@@ -601,7 +601,7 @@ static int sundance_probe ( struct nic *nic, struct pci_device *pci ) {
sdc->nic_name = pci->id->name;
sdc->mtu = mtu;
- pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id);
+ pci_read_config_byte(pci, PCI_REVISION, &sdc->pci_rev_id);
DBG ( "Device revision id: %hx\n", sdc->pci_rev_id );
diff --git a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c
index 32ca1609c..42bfa2d99 100644
--- a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c
+++ b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.c
@@ -928,6 +928,7 @@ static struct pci_device_id tg3_nics[] = {
PCI_ROM(0x14e4, 0x16b6, "14e4-16b6", "14e4-16b6", 0),
PCI_ROM(0x14e4, 0x1657, "14e4-1657", "14e4-1657", 0),
PCI_ROM(0x14e4, 0x165f, "14e4-165f", "14e4-165f", 0),
+ PCI_ROM(0x14e4, 0x1686, "14e4-1686", "14e4-1686", 0),
PCI_ROM(0x1148, 0x4400, "1148-4400", "1148-4400", 0),
PCI_ROM(0x1148, 0x4500, "1148-4500", "1148-4500", 0),
PCI_ROM(0x173b, 0x03e8, "173b-03e8", "173b-03e8", 0),
diff --git a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h
index 660368394..2b85b065b 100644
--- a/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h
+++ b/qemu/roms/ipxe/src/drivers/net/tg3/tg3.h
@@ -131,6 +131,10 @@
#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e
#define PCI_DEVICE_ID_TIGON3_5906 0x1712
#define PCI_DEVICE_ID_TIGON3_5906M 0x1713
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_IBM 0x1014
+#define PCI_VENDOR_ID_DELL 0x1028
+#define PCI_VENDOR_ID_3COM 0x10b7
/* </pci_ids.h> */
#define SPEED_10 10
@@ -185,6 +189,7 @@
#define TG3PCI_DEVICE_TIGON3_57761 0x16b0
#define TG3PCI_DEVICE_TIGON3_57762 0x1682
#define TG3PCI_DEVICE_TIGON3_57765 0x16b4
+#define TG3PCI_DEVICE_TIGON3_57766 0x1686
#define TG3PCI_DEVICE_TIGON3_57791 0x16b2
#define TG3PCI_DEVICE_TIGON3_57795 0x16b6
#define TG3PCI_DEVICE_TIGON3_5719 0x1657
diff --git a/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c b/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c
index 3a481aba3..50353cf36 100644
--- a/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c
+++ b/qemu/roms/ipxe/src/drivers/net/tg3/tg3_hw.c
@@ -436,6 +436,7 @@ int tg3_get_invariants(struct tg3 *tp)
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57761 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57762 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57765 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_57766 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57791 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57795)
pci_read_config_dword(tp->pdev,
diff --git a/qemu/roms/ipxe/src/drivers/net/virtio-net.c b/qemu/roms/ipxe/src/drivers/net/virtio-net.c
index d5fd81979..533ccb0c6 100644
--- a/qemu/roms/ipxe/src/drivers/net/virtio-net.c
+++ b/qemu/roms/ipxe/src/drivers/net/virtio-net.c
@@ -20,7 +20,7 @@
* See the COPYING file in the top-level directory.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <stdlib.h>
@@ -131,8 +131,8 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
},
};
- DBGC ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n",
- virtnet, iobuf, vq_idx );
+ DBGC2 ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n",
+ virtnet, iobuf, vq_idx );
vring_add_buf ( vq, list, out, in, iobuf, 0 );
vring_kick ( virtnet->ioaddr, vq, 1 );
@@ -256,8 +256,8 @@ static void virtnet_process_tx_packets ( struct net_device *netdev ) {
while ( vring_more_used ( tx_vq ) ) {
struct io_buffer *iobuf = vring_get_buf ( tx_vq, NULL );
- DBGC ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n",
- virtnet, iobuf );
+ DBGC2 ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n",
+ virtnet, iobuf );
netdev_tx_complete ( netdev, iobuf );
}
@@ -283,8 +283,8 @@ static void virtnet_process_rx_packets ( struct net_device *netdev ) {
iob_unput ( iobuf, RX_BUF_SIZE );
iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) );
- DBGC ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n",
- virtnet, iobuf, iob_len ( iobuf ) );
+ DBGC2 ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n",
+ virtnet, iobuf, iob_len ( iobuf ) );
/* Pass completed packet to the network stack */
netdev_rx ( netdev, iobuf );
diff --git a/qemu/roms/ipxe/src/drivers/net/vmxnet3.c b/qemu/roms/ipxe/src/drivers/net/vmxnet3.c
index 31082bf6f..8d4f4b843 100644
--- a/qemu/roms/ipxe/src/drivers/net/vmxnet3.c
+++ b/qemu/roms/ipxe/src/drivers/net/vmxnet3.c
@@ -15,9 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <errno.h>
diff --git a/qemu/roms/ipxe/src/drivers/net/vmxnet3.h b/qemu/roms/ipxe/src/drivers/net/vmxnet3.h
index db313d4b8..a1671d9dd 100644
--- a/qemu/roms/ipxe/src/drivers/net/vmxnet3.h
+++ b/qemu/roms/ipxe/src/drivers/net/vmxnet3.h
@@ -18,9 +18,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
*/
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
diff --git a/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c b/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c
index bf20ec43c..d50ac05b5 100644
--- a/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c
+++ b/qemu/roms/ipxe/src/drivers/net/vxge/vxge.c
@@ -5,10 +5,11 @@
* as "vxge" even though the code is in vxge_* named files.
*/
-FILE_LICENCE(GPL2_OR_LATER);
+FILE_LICENCE(GPL2_OR_LATER_OR_UBDL);
#include <ipxe/pci.h>
+PROVIDE_REQUIRING_SYMBOL();
REQUIRE_OBJECT(vxge_main);
/** vxge PCI IDs for util/parserom.pl which are put into bin/NIC */
diff --git a/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c b/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c
index 130eab617..8b099c0e2 100644
--- a/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c
+++ b/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c
@@ -509,7 +509,7 @@ vxge_probe(struct pci_device *pdev)
vxge_debug(VXGE_INFO, "vxge_probe for device " PCI_FMT "\n",
PCI_ARGS(pdev));
- pci_read_config_byte(pdev, PCI_REVISION_ID, &revision);
+ pci_read_config_byte(pdev, PCI_REVISION, &revision);
titan1 = is_titan1(pdev->device, revision);
mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
diff --git a/qemu/roms/ipxe/src/drivers/net/w89c840.c b/qemu/roms/ipxe/src/drivers/net/w89c840.c
index ce638ab99..d8144a8ce 100644
--- a/qemu/roms/ipxe/src/drivers/net/w89c840.c
+++ b/qemu/roms/ipxe/src/drivers/net/w89c840.c
@@ -641,7 +641,9 @@ static int w89c840_probe ( struct nic *nic, struct pci_device *p ) {
ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
+#define PCI_VENDOR_ID_WINBOND2 0x1050
#define PCI_DEVICE_ID_WINBOND2_89C840 0x0840
+#define PCI_VENDOR_ID_COMPEX 0x11f6
#define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011
/* From Matt Hortman <mbhortman@acpthinclient.com> */