]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Work around bugs in Emulex NII driver
authorFabrice Bacchella <fbacchella@spamcop.net>
Thu, 13 Aug 2015 10:44:21 +0000 (12:44 +0200)
committerMichael Brown <mcb30@ipxe.org>
Mon, 17 Aug 2015 14:57:05 +0000 (15:57 +0100)
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/net/efi/nii.c

index 9e0cb60ba249a7382eee9687d58e9df3fb38ae05..b91848f5c0a23eda5aa5c6ff35037aba63f599f1 100644 (file)
@@ -575,9 +575,10 @@ 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;
@@ -600,8 +601,7 @@ static int nii_initialise ( struct nii_nic *nii ) {
        memset ( &db, 0, sizeof ( db ) );
 
        /* Issue command */
-       op = NII_OP ( PXE_OPCODE_INITIALIZE,
-                     PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE );
+       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 );
@@ -618,6 +618,36 @@ static int nii_initialise ( struct nii_nic *nii ) {
        return rc;
 }
 
+/**
+ * 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
  *
@@ -650,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;
 
@@ -658,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 ) );
@@ -689,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 ) );
@@ -717,6 +756,7 @@ 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;
@@ -724,10 +764,13 @@ static int nii_set_rx_filters ( struct nii_nic *nii ) {
 
        /* Construct receive filter set */
        flags = ( 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 );
+                 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, flags );
@@ -752,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;
 
@@ -768,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 ) );
@@ -924,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 */