* 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;
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 );
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
*
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;
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 ) );
*/
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 ) );
* @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;
/* 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 );
struct io_buffer *iobuf ) {
struct nii_nic *nii = netdev->priv;
PXE_CPB_TRANSMIT cpb;
+ unsigned int op;
int stat;
int rc;
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 ) );
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 */