diff options
Diffstat (limited to 'src/drivers/net')
| -rw-r--r-- | src/drivers/net/ecm.c | 5 | ||||
| -rw-r--r-- | src/drivers/net/efi/nii.c | 59 | ||||
| -rw-r--r-- | src/drivers/net/efi/snp.c | 66 | ||||
| -rw-r--r-- | src/drivers/net/efi/snponly.c | 2 | ||||
| -rw-r--r-- | src/drivers/net/ena.c | 93 | ||||
| -rw-r--r-- | src/drivers/net/ena.h | 46 | ||||
| -rw-r--r-- | src/drivers/net/intel.c | 20 | ||||
| -rw-r--r-- | src/drivers/net/intel.h | 10 | ||||
| -rw-r--r-- | src/drivers/net/intelx.c | 1 | ||||
| -rw-r--r-- | src/drivers/net/netfront.c | 49 | ||||
| -rw-r--r-- | src/drivers/net/netfront.h | 5 | ||||
| -rw-r--r-- | src/drivers/net/realtek.c | 4 | ||||
| -rw-r--r-- | src/drivers/net/realtek.h | 5 |
13 files changed, 316 insertions, 49 deletions
diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 68ac962ab..ab1f98370 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -121,10 +121,9 @@ int ecm_fetch_mac ( struct usb_function *func, } /* Apply system-specific MAC address as current link-layer - * address, if present and not already used. + * address, if present. */ - if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) && - ! find_netdev_by_ll_addr ( ðernet_protocol, amac ) ) { + if ( ( rc = acpi_mac ( amac ) ) == 0 ) { memcpy ( netdev->ll_addr, amac, ETH_ALEN ); DBGC ( usb, "USB %s using system-specific MAC %s\n", func->name, eth_ntoa ( netdev->ll_addr ) ); diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index 833462e77..be5bce4b4 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -222,7 +222,7 @@ static int nii_pci_open ( struct nii_nic *nii ) { /* Locate PCI I/O protocol */ if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid, - &pci_device ) ) != 0 ) { + &pci_device, 0 ) ) != 0 ) { DBGC ( nii, "NII %s could not locate PCI I/O protocol: %s\n", nii->dev.name, strerror ( rc ) ); goto err_locate; @@ -921,18 +921,17 @@ static int nii_set_station_address ( struct nii_nic *nii, * Set receive filters * * @v nii NII NIC + * @v flags Flags * @ret rc Return status code */ -static int nii_set_rx_filters ( struct nii_nic *nii ) { +static int nii_set_rx_filters ( struct nii_nic *nii, unsigned int flags ) { 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 ); + flags |= 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 ) @@ -944,8 +943,12 @@ static int nii_set_rx_filters ( struct nii_nic *nii ) { 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 %#04x: %s\n", - nii->dev.name, flags, strerror ( rc ) ); + DBGC ( nii, "NII %s could not %s%sable receive filters " + "%#04x: %s\n", nii->dev.name, + ( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_ENABLE ) ? + "en" : "" ), + ( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_DISABLE ) ? + "dis" : "" ), flags, strerror ( rc ) ); return rc; } @@ -953,6 +956,28 @@ static int nii_set_rx_filters ( struct nii_nic *nii ) { } /** + * Enable receive filters + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_enable_rx_filters ( struct nii_nic *nii ) { + + return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_ENABLE ); +} + +/** + * Disable receive filters + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_disable_rx_filters ( struct nii_nic *nii ) { + + return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_DISABLE ); +} + +/** * Transmit packet * * @v netdev Network device @@ -1175,13 +1200,25 @@ static int nii_open ( struct net_device *netdev ) { /* Treat as non-fatal */ } - /* Set receive filters */ - if ( ( rc = nii_set_rx_filters ( nii ) ) != 0 ) - goto err_set_rx_filters; + /* Disable receive filters + * + * We have no reason to disable receive filters here (or + * anywhere), but some NII drivers have a bug which prevents + * packets from being received unless we attempt to disable + * the receive filters. + * + * Ignore any failures, since we genuinely don't care if the + * NII driver cannot disable the filters. + */ + nii_disable_rx_filters ( nii ); + + /* Enable receive filters */ + if ( ( rc = nii_enable_rx_filters ( nii ) ) != 0 ) + goto err_enable_rx_filters; return 0; - err_set_rx_filters: + err_enable_rx_filters: nii_shutdown ( nii ); err_initialise: return rc; diff --git a/src/drivers/net/efi/snp.c b/src/drivers/net/efi/snp.c index fbd606902..1920cdbc5 100644 --- a/src/drivers/net/efi/snp.c +++ b/src/drivers/net/efi/snp.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_snp.h> +#include <ipxe/efi/efi_utils.h> #include "snpnet.h" #include "nii.h" @@ -40,31 +41,46 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Check to see if driver supports a device * * @v device EFI device handle + * @v protocol Protocol GUID * @ret rc Return status code */ -static int snp_supported ( EFI_HANDLE device ) { +static int snp_nii_supported ( EFI_HANDLE device, EFI_GUID *protocol ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE parent; EFI_STATUS efirc; + int rc; /* Check that this is not a device we are providing ourselves */ if ( find_snpdev ( device ) != NULL ) { - DBGCP ( device, "SNP %s is provided by this binary\n", + DBGCP ( device, "HANDLE %s is provided by this binary\n", efi_handle_name ( device ) ); return -ENOTTY; } - /* Test for presence of simple network protocol */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_simple_network_protocol_guid, + /* Test for presence of protocol */ + if ( ( efirc = bs->OpenProtocol ( device, protocol, NULL, efi_image_handle, device, EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){ - DBGCP ( device, "SNP %s is not an SNP device\n", - efi_handle_name ( device ) ); + DBGCP ( device, "HANDLE %s is not a %s device\n", + efi_handle_name ( device ), + efi_guid_ntoa ( protocol ) ); return -EEFI ( efirc ); } - DBGC ( device, "SNP %s is an SNP device\n", - efi_handle_name ( device ) ); + /* Check that there are no instances of this protocol further + * up this device path. + */ + if ( ( rc = efi_locate_device ( device, protocol, + &parent, 1 ) ) == 0 ) { + DBGC2 ( device, "HANDLE %s has %s-supporting parent ", + efi_handle_name ( device ), + efi_guid_ntoa ( protocol ) ); + DBGC2 ( device, "%s\n", efi_handle_name ( parent ) ); + return -ENOTTY; + } + + DBGC ( device, "HANDLE %s is a %s device\n", + efi_handle_name ( device ), efi_guid_ntoa ( protocol ) ); return 0; } @@ -74,30 +90,20 @@ static int snp_supported ( EFI_HANDLE device ) { * @v device EFI device handle * @ret rc Return status code */ -static int nii_supported ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_STATUS efirc; +static int snp_supported ( EFI_HANDLE device ) { - /* Check that this is not a device we are providing ourselves */ - if ( find_snpdev ( device ) != NULL ) { - DBGCP ( device, "NII %s is provided by this binary\n", - efi_handle_name ( device ) ); - return -ENOTTY; - } + return snp_nii_supported ( device, &efi_simple_network_protocol_guid ); +} - /* Test for presence of NII protocol */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_nii31_protocol_guid, - NULL, efi_image_handle, device, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){ - DBGCP ( device, "NII %s is not an NII device\n", - efi_handle_name ( device ) ); - return -EEFI ( efirc ); - } - DBGC ( device, "NII %s is an NII device\n", - efi_handle_name ( device ) ); +/** + * Check to see if driver supports a device + * + * @v device EFI device handle + * @ret rc Return status code + */ +static int nii_supported ( EFI_HANDLE device ) { - return 0; + return snp_nii_supported ( device, &efi_nii31_protocol_guid ); } /** EFI SNP driver */ diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c index cb7ea1bbc..674e0a050 100644 --- a/src/drivers/net/efi/snponly.c +++ b/src/drivers/net/efi/snponly.c @@ -80,7 +80,7 @@ static int chained_locate ( struct chained_protocol *chained ) { /* Locate handle supporting this protocol */ if ( ( rc = efi_locate_device ( device, chained->protocol, - &parent ) ) != 0 ) { + &parent, 0 ) ) != 0 ) { DBGC ( device, "CHAINED %s does not support %s: %s\n", efi_handle_name ( device ), efi_guid_ntoa ( chained->protocol ), strerror ( rc ) ); diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 22e7e1e30..7ce5b9eb9 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -351,6 +351,90 @@ static int ena_admin ( struct ena_nic *ena, union ena_aq_req *req, } /** + * Set async event notification queue config + * + * @v ena ENA device + * @v enabled Bitmask of the groups to enable + * @ret rc Return status code + */ +static int ena_set_aenq_config ( struct ena_nic *ena, uint32_t enabled ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + union ena_feature *feature; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_SET_FEATURE; + req->set_feature.id = ENA_AENQ_CONFIG; + feature = &req->set_feature.feature; + feature->aenq.enabled = cpu_to_le32 ( enabled ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Create async event notification queue + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_create_async ( struct ena_nic *ena ) { + size_t aenq_len = ( ENA_AENQ_COUNT * sizeof ( ena->aenq.evt[0] ) ); + int rc; + + /* Allocate async event notification queue */ + ena->aenq.evt = malloc_phys ( aenq_len, aenq_len ); + if ( ! ena->aenq.evt ) { + rc = -ENOMEM; + goto err_alloc_aenq; + } + memset ( ena->aenq.evt, 0, aenq_len ); + + /* Program queue address and capabilities */ + ena_set_base ( ena, ENA_AENQ_BASE, ena->aenq.evt ); + ena_set_caps ( ena, ENA_AENQ_CAPS, ENA_AENQ_COUNT, + sizeof ( ena->aenq.evt[0] ) ); + + DBGC ( ena, "ENA %p AENQ [%08lx,%08lx)\n", + ena, virt_to_phys ( ena->aenq.evt ), + ( virt_to_phys ( ena->aenq.evt ) + aenq_len ) ); + + /* Disable all events */ + if ( ( rc = ena_set_aenq_config ( ena, 0 ) ) != 0 ) + goto err_set_aenq_config; + + return 0; + + err_set_aenq_config: + ena_clear_caps ( ena, ENA_AENQ_CAPS ); + free_phys ( ena->aenq.evt, aenq_len ); + err_alloc_aenq: + return rc; +} + +/** + * Destroy async event notification queue + * + * @v ena ENA device + */ +static void ena_destroy_async ( struct ena_nic *ena ) { + size_t aenq_len = ( ENA_AENQ_COUNT * sizeof ( ena->aenq.evt[0] ) ); + + /* Clear queue capabilities */ + ena_clear_caps ( ena, ENA_AENQ_CAPS ); + wmb(); + + /* Free queue */ + free_phys ( ena->aenq.evt, aenq_len ); + DBGC ( ena, "ENA %p AENQ destroyed\n", ena ); +} + +/** * Create submission queue * * @v ena ENA device @@ -1098,6 +1182,10 @@ static int ena_probe ( struct pci_device *pci ) { if ( ( rc = ena_create_admin ( ena ) ) != 0 ) goto err_create_admin; + /* Create async event notification queue */ + if ( ( rc = ena_create_async ( ena ) ) != 0 ) + goto err_create_async; + /* Set host attributes */ if ( ( rc = ena_set_host_attributes ( ena ) ) != 0 ) goto err_set_host_attributes; @@ -1121,6 +1209,8 @@ static int ena_probe ( struct pci_device *pci ) { err_register_netdev: err_get_device_attributes: err_set_host_attributes: + ena_destroy_async ( ena ); + err_create_async: ena_destroy_admin ( ena ); err_create_admin: ena_reset ( ena ); @@ -1148,6 +1238,9 @@ static void ena_remove ( struct pci_device *pci ) { /* Unregister network device */ unregister_netdev ( netdev ); + /* Destroy async event notification queue */ + ena_destroy_async ( ena ); + /* Destroy admin queues */ ena_destroy_admin ( ena ); diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index 4e1896e86..0f280c700 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -24,6 +24,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Number of admin completion queue entries */ #define ENA_ACQ_COUNT 2 +/** Number of async event notification queue entries */ +#define ENA_AENQ_COUNT 2 + /** Number of transmit queue entries */ #define ENA_TX_COUNT 16 @@ -60,6 +63,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum time to wait for admin requests */ #define ENA_ADMIN_MAX_WAIT_MS 5000 +/** Async event notification queue capabilities register */ +#define ENA_AENQ_CAPS 0x34 + +/** Async event notification queue base address register */ +#define ENA_AENQ_BASE 0x38 + /** Device control register */ #define ENA_CTRL 0x54 #define ENA_CTRL_RESET 0x00000001UL /**< Reset */ @@ -130,6 +139,17 @@ struct ena_device_attributes { uint32_t mtu; } __attribute__ (( packed )); +/** Async event notification queue config */ +#define ENA_AENQ_CONFIG 26 + +/** Async event notification queue config */ +struct ena_aenq_config { + /** Bitmask of supported AENQ groups (device -> host) */ + uint32_t supported; + /** Bitmask of enabled AENQ groups (host -> device) */ + uint32_t enabled; +} __attribute__ (( packed )); + /** Host attributes */ #define ENA_HOST_ATTRIBUTES 28 @@ -208,6 +228,8 @@ struct ena_host_info { union ena_feature { /** Device attributes */ struct ena_device_attributes device; + /** Async event notification queue config */ + struct ena_aenq_config aenq; /** Host attributes */ struct ena_host_attributes host; }; @@ -506,6 +528,28 @@ struct ena_acq { unsigned int phase; }; +/** Async event notification queue event */ +struct ena_aenq_event { + /** Type of event */ + uint16_t group; + /** ID of event */ + uint16_t syndrome; + /** Phase */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[3]; + /** Timestamp */ + uint64_t timestamp; + /** Additional event data */ + uint8_t data[48]; +} __attribute__ (( packed )); + +/** Async event notification queue */ +struct ena_aenq { + /** Events */ + struct ena_aenq_event *evt; +}; + /** Transmit submission queue entry */ struct ena_tx_sqe { /** Length */ @@ -702,6 +746,8 @@ struct ena_nic { struct ena_aq aq; /** Admin completion queue */ struct ena_acq acq; + /** Async event notification queue */ + struct ena_aenq aenq; /** Transmit queue */ struct ena_qp tx; /** Receive queue */ diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index ea3ebf68d..46527bdbb 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -290,6 +290,18 @@ static int intel_reset ( struct intel_nic *intel ) { pba, readl ( intel->regs + INTEL_PBA ) ); } + /* The Intel I210's packet buffer size registers reset only on + * power up. If an operating system changes these but then + * the computer recieves a reset signal without losing power, + * the registers will stay the same (but be incompatible with + * other register defaults), thus making the device unable to + * pass traffic. + */ + if ( intel->flags & INTEL_PBSIZE_RST ) { + writel ( INTEL_RXPBS_I210, intel->regs + INTEL_RXPBS ); + writel ( INTEL_TXPBS_I210, intel->regs + INTEL_TXPBS ); + } + /* Always reset MAC. Required to reset the TX and RX rings. */ writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); @@ -1139,7 +1151,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ), PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ), PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ), - PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ), + PCI_ROM ( 0x8086, 0x1533, "i210", "I210", INTEL_PBSIZE_RST ), PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ), PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), @@ -1147,7 +1159,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ), - PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), + PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", INTEL_PBSIZE_RST ), PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), @@ -1173,6 +1185,10 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15fa, "i219v-14", "I219-V (14)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15fb, "i219lm-13", "I219-LM (13)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15fc, "i219v-13", "I219-V (13)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1c, "i219lm-17", "I219-LM (17)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1d, "i219v-17", "I219-V (17)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1e, "i219lm-16", "I219-LM (16)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1f, "i219v-16", "I219-V (16)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index 4f51a80f6..29cf3a7d8 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -138,6 +138,10 @@ struct intel_descriptor { /** Packet Buffer Size */ #define INTEL_PBS 0x01008UL +/** Receive packet buffer size */ +#define INTEL_RXPBS 0x02404UL +#define INTEL_RXPBS_I210 0x000000a2UL /**< I210 power-up default */ + /** Receive Descriptor register block */ #define INTEL_RD 0x02800UL @@ -154,6 +158,10 @@ struct intel_descriptor { /** Receive buffer length */ #define INTEL_RX_MAX_LEN 2048 +/** Transmit packet buffer size */ +#define INTEL_TXPBS 0x03404UL +#define INTEL_TXPBS_I210 0x04000014UL /**< I210 power-up default */ + /** Transmit Descriptor register block */ #define INTEL_TD 0x03800UL @@ -319,6 +327,8 @@ enum intel_flags { INTEL_NO_ASDE = 0x0008, /** Reset may cause a complete device hang */ INTEL_RST_HANG = 0x0010, + /** PBSIZE registers must be explicitly reset */ + INTEL_PBSIZE_RST = 0x0020, }; /** The i219 has a seriously broken reset mechanism */ diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index f4dad8859..343d01374 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -473,6 +473,7 @@ static struct pci_device_id intelx_nics[] = { 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, 0x151c, "82599-tn", "82599 (TN)", 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 ), diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index 1203e585c..90930a5a3 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -59,6 +59,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); EUNIQ ( EINFO_EIO, ( -(status) & 0x1f ), \ EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED ) +/** List of netfront devices */ +static LIST_HEAD ( netfront_devices ); + /****************************************************************************** * * XenStore interface @@ -952,6 +955,7 @@ static int netfront_probe ( struct xen_device *xendev ) { netdev->dev = &xendev->dev; netfront = netdev->priv; netfront->xendev = xendev; + netfront->netdev = netdev; INIT_LIST_HEAD ( &netfront->rx_partial ); DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n", xendev->key, xendev->backend, xendev->backend_id ); @@ -991,9 +995,13 @@ static int netfront_probe ( struct xen_device *xendev ) { /* Set initial link state */ netdev_link_down ( netdev ); + /* Add to list of netfront devices */ + list_add_tail ( &netfront->list, &netfront_devices ); + xen_set_drvdata ( xendev, netdev ); return 0; + list_del ( &netfront->list ); unregister_netdev ( netdev ); err_register_netdev: err_read_mac: @@ -1015,6 +1023,9 @@ static void netfront_remove ( struct xen_device *xendev ) { struct netfront_nic *netfront = netdev->priv; struct xen_hypervisor *xen = xendev->xen; + /* Remove from list of netfront devices */ + list_del ( &netfront->list ); + /* Unregister network device */ unregister_netdev ( netdev ); @@ -1033,3 +1044,41 @@ struct xen_driver netfront_driver __xen_driver = { .probe = netfront_probe, .remove = netfront_remove, }; + +/****************************************************************************** + * + * Emulated PCI device inhibitor + * + ****************************************************************************** + */ + +/** + * Inhibit emulated PCI devices + * + * @v netdev Network device + * @ret rc Return status code + */ +static int netfront_net_probe ( struct net_device *netdev ) { + struct netfront_nic *netfront; + + /* Inhibit emulated PCI devices matching an existing netfront device */ + list_for_each_entry ( netfront, &netfront_devices, list ) { + if ( ( netdev->dev != netfront->netdev->dev ) && + ( netdev->ll_protocol->ll_addr_len == ETH_ALEN ) && + ( memcmp ( netdev->hw_addr, netfront->netdev->hw_addr, + ETH_ALEN ) == 0 ) ) { + DBGC ( netfront, "NETFRONT %s inhibiting emulated %s " + "%s\n", netfront->xendev->key, + netdev->dev->driver_name, netdev->dev->name ); + return -EEXIST; + } + } + + return 0; +} + +/** Emulated PCI device inhibitor driver */ +struct net_driver netfront_net_driver __net_driver = { + .name = "netfront", + .probe = netfront_net_probe, +}; diff --git a/src/drivers/net/netfront.h b/src/drivers/net/netfront.h index dca3ff1c5..de16d5291 100644 --- a/src/drivers/net/netfront.h +++ b/src/drivers/net/netfront.h @@ -159,6 +159,11 @@ struct netfront_nic { /** Grant references */ grant_ref_t refs[NETFRONT_REF_COUNT]; + /** Network device */ + struct net_device *netdev; + /** List of netfront NICs */ + struct list_head list; + /** Transmit ring */ struct netfront_ring tx; /** Transmit front ring */ diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index a43efb68b..80442ab83 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -1067,11 +1067,15 @@ static void realtek_detect ( struct realtek_nic *rtl ) { * Note that enabling DAC seems to cause bizarre behaviour * (lockups, garbage data on the wire) on some systems, even * if only 32-bit addresses are used. + * + * Disable VLAN offload, since some cards seem to have it + * enabled by default. */ cpcr = readw ( rtl->regs + RTL_CPCR ); cpcr |= ( RTL_CPCR_MULRW | RTL_CPCR_CPRX | RTL_CPCR_CPTX ); if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) cpcr |= RTL_CPCR_DAC; + cpcr &= ~RTL_CPCR_VLAN; writew ( cpcr, rtl->regs + RTL_CPCR ); check_cpcr = readw ( rtl->regs + RTL_CPCR ); diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h index d4642fd76..d50e349b0 100644 --- a/src/drivers/net/realtek.h +++ b/src/drivers/net/realtek.h @@ -228,8 +228,9 @@ enum realtek_legacy_status { /** C+ Command Register (word) */ #define RTL_CPCR 0xe0 -#define RTL_CPCR_DAC 0x0010 /**< PCI Dual Address Cycle Enable */ -#define RTL_CPCR_MULRW 0x0008 /**< PCI Multiple Read/Write Enable */ +#define RTL_CPCR_VLAN 0x0040 /**< VLAN tag stripping enable */ +#define RTL_CPCR_DAC 0x0010 /**< PCI Dual Address Cycle enable */ +#define RTL_CPCR_MULRW 0x0008 /**< PCI Multiple Read/Write enable */ #define RTL_CPCR_CPRX 0x0002 /**< C+ receive enable */ #define RTL_CPCR_CPTX 0x0001 /**< C+ transmit enable */ |
