/** * Get station addresses * * @v nii NII NIC * @v netdev Network device to fill in * @ret rc Return status code */ static int nii_get_station_address ( struct nii_nic *nii, struct net_device *netdev ) { PXE_DB_STATION_ADDRESS db; int stat; int rc; /* Initialise UNDI */ if ( ( rc = nii_initialise ( nii ) ) != 0 ) goto err_initialise; /* Issue command */ if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_STATION_ADDRESS, &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not get station address: %s\n", nii->dev.name, strerror ( rc ) ); goto err_station_address; } /* Copy MAC addresses */ memcpy ( netdev->ll_addr, db.StationAddr, netdev->ll_protocol->ll_addr_len ); memcpy ( netdev->hw_addr, db.PermanentAddr, netdev->ll_protocol->hw_addr_len ); memcpy ( nii->broadcast, db.BroadcastAddr, sizeof ( nii->broadcast ) ); err_station_address: nii_shutdown ( nii ); err_initialise: return rc; }
/** * Poll for completed packets * * @v netdev Network device */ static void nii_poll ( struct net_device *netdev ) { struct nii_nic *nii = netdev->priv; PXE_DB_GET_STATUS db; unsigned int op; 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 | ( 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", nii->dev.name, strerror ( rc ) ); return; } /* Process any TX completions */ if ( nii->txbuf ) nii_poll_tx ( netdev, stat ); /* Process any RX completions */ nii_poll_rx ( netdev ); /* Check for link state changes */ if ( nii->media ) nii_poll_link ( netdev, stat ); }
/** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int nii_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct nii_nic *nii = netdev->priv; PXE_CPB_TRANSMIT cpb; int stat; int rc; /* Defer the packet if there is already a transmission in progress */ if ( nii->txbuf ) { netdev_tx_defer ( netdev, iobuf ); return 0; } /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); cpb.FrameAddr = virt_to_bus ( iobuf->data ); cpb.DataLen = iob_len ( iobuf ); cpb.MediaheaderLen = netdev->ll_protocol->ll_header_len; /* Transmit packet */ if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_TRANSMIT, &cpb, sizeof ( cpb ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not transmit: %s\n", nii->dev.name, strerror ( rc ) ); return rc; } nii->txbuf = iobuf; return 0; }
/** * Set receive filters * * @v nii NII NIC * @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, 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 ) ); return rc; } return 0; }
/** * Set station address * * @v nii NII NIC * @v netdev Network device * @ret rc Return status code */ 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 */ 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 ) ); return rc; } return 0; }
/** * Stop UNDI * * @v nii NII NIC */ static void nii_stop_undi ( struct nii_nic *nii ) { int stat; int rc; /* Issue command */ if ( ( stat = nii_issue ( nii, PXE_OPCODE_STOP ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not stop: %s\n", nii->dev.name, strerror ( rc ) ); /* Nothing we can do about it */ return; } }
/** * Poll for received packets * * @v netdev Network device */ static void nii_poll_rx ( struct net_device *netdev ) { struct nii_nic *nii = netdev->priv; PXE_CPB_RECEIVE cpb; PXE_DB_RECEIVE db; unsigned int quota; int stat; int rc; /* Retrieve up to NII_RX_QUOTA packets */ for ( quota = NII_RX_QUOTA ; quota ; quota-- ) { /* Allocate buffer, if required */ if ( ! nii->rxbuf ) { nii->rxbuf = alloc_iob ( nii->mtu ); if ( ! nii->rxbuf ) { /* Leave for next poll */ break; } } /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); cpb.BufferAddr = virt_to_bus ( nii->rxbuf->data ); cpb.BufferLen = iob_tailroom ( nii->rxbuf ); /* Issue command */ if ( ( stat = nii_issue_cpb_db ( nii, PXE_OPCODE_RECEIVE, &cpb, sizeof ( cpb ), &db, sizeof ( db ) ) ) < 0 ) { /* PXE_STATCODE_NO_DATA is just the usual "no packet" * status indicator; ignore it. */ if ( stat == -PXE_STATCODE_NO_DATA ) break; /* Anything else is an error */ rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not receive: %s\n", nii->dev.name, strerror ( rc ) ); netdev_rx_err ( netdev, NULL, rc ); break; } /* Hand off to network stack */ iob_put ( nii->rxbuf, db.FrameLen ); netdev_rx ( netdev, nii->rxbuf ); nii->rxbuf = NULL; } }
/** * Shut down UNDI * * @v nii NII NIC */ static void nii_shutdown ( struct nii_nic *nii ) { int stat; int rc; /* Issue command */ if ( ( stat = nii_issue ( nii, PXE_OPCODE_SHUTDOWN ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not shut down: %s\n", nii->dev.name, strerror ( rc ) ); /* Leak memory to avoid corruption */ return; } /* Free buffer */ ufree ( nii->buffer ); }
/** * Set receive filters * * @v nii NII NIC * @ret rc Return status code */ static int nii_set_rx_filters ( struct nii_nic *nii ) { unsigned int op; int stat; int rc; /* 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 ) ); 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 ) ); return rc; } return 0; }
/** * Initialise UNDI * * @v nii NII NIC * @ret rc Return status code */ static int nii_initialise ( struct nii_nic *nii ) { PXE_CPB_INITIALIZE cpb; PXE_DB_INITIALIZE db; unsigned int op; int stat; int rc; /* Allocate memory buffer */ nii->buffer = umalloc ( nii->buffer_len ); if ( ! nii->buffer ) { rc = -ENOMEM; goto err_alloc; } /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); 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_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 ) ); goto err_initialize; } return 0; err_initialize: ufree ( nii->buffer ); err_alloc: return rc; }
/** * Set station address * * @v nii NII NIC * @v netdev Network device * @ret rc Return status code */ static int nii_set_station_address ( struct nii_nic *nii, struct net_device *netdev ) { PXE_CPB_STATION_ADDRESS cpb; int stat; int rc; /* 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 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not set station address: %s\n", nii->dev.name, strerror ( rc ) ); return rc; } return 0; }
/** * Get initialisation information * * @v nii NII NIC * @v netdev Network device to fill in * @ret rc Return status code */ static int nii_get_init_info ( struct nii_nic *nii, struct net_device *netdev ) { PXE_DB_GET_INIT_INFO db; int stat; int rc; /* Issue command */ if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_GET_INIT_INFO, &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not get initialisation info: %s\n", nii->dev.name, strerror ( rc ) ); return rc; } /* Determine link layer protocol */ switch ( db.IFtype ) { case PXE_IFTYPE_ETHERNET : netdev->ll_protocol = ðernet_protocol; break; default: DBGC ( nii, "NII %s unknown interface type %#02x\n", nii->dev.name, db.IFtype ); return -ENOTSUP; } /* Sanity checks */ assert ( db.MediaHeaderLen == netdev->ll_protocol->ll_header_len ); assert ( db.HWaddrLen == netdev->ll_protocol->hw_addr_len ); assert ( db.HWaddrLen == netdev->ll_protocol->ll_addr_len ); /* Extract parameters */ 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; }
/** * Start UNDI * * @v nii NII NIC * @ret rc Return status code */ static int nii_start_undi ( struct nii_nic *nii ) { PXE_CPB_START_31 cpb; int stat; int rc; /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); cpb.Delay = ( ( intptr_t ) nii_delay ); cpb.Block = ( ( intptr_t ) nii_block ); cpb.Mem_IO = ( ( intptr_t ) nii_io ); cpb.Unique_ID = ( ( intptr_t ) nii ); /* Issue command */ if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_START, &cpb, sizeof ( cpb ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not start: %s\n", nii->dev.name, strerror ( rc ) ); return rc; } return 0; }