diff options
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/net')
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, ¶ms, + 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> */ |