📄 realtekr1000.cpp
字号:
/* This driver based on R1000 Linux Driver for Realtek controllers. * It's not supported by Realtek company, so use it for your own risk. * 2006 (c) Dmitri Arekhta (DaemonES@gmail.com) * * 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. * ***************************************************************** * * MODIFIED by PSYSTAR, 2008 -- (Rudy Pedraza) * -- all changes released under GPL as required * -- changes made to code are copyright PSYSTAR Corporation, 2008 **** Enhancement Log * - added sleep/wake DHCP fix for * - changed tx/rx interrupt handling, 2x speedup * - added support for multiple NIC's, driver didnt play nice before * - fixed com.apple.kernel & com.apple.kpi dependencies, you cant use both (warning now, future error) * - cleaned up Info.plist, fixed matching */#include "RealtekR1000.h"#define BaseClass IOEthernetController#define RELEASE(x) do { if(x) { (x)->release(); (x) = 0; } } while(0)OSDefineMetaClassAndStructors(RealtekR1000, IOEthernetController)/* Some additional structs */const static struct RtlChipInfo { const char *name; u8 mcfg; /* depend on documents of Realtek */ u32 RxConfigMask; /* should clear the bits supported by this chip */} rtl_chip_info[] = { { "RTL8169", MCFG_METHOD_1, 0xff7e1880 }, { "RTL8169S/8110S", MCFG_METHOD_2, 0xff7e1880 }, { "RTL8169S/8110S", MCFG_METHOD_3, 0xff7e1880 }, { "RTL8169SB/8110SB", MCFG_METHOD_4, 0xff7e1880 }, { "RTL8169SC/8110SC", MCFG_METHOD_5, 0xff7e1880 }, { "RTL8168B/8111B", MCFG_METHOD_11, 0xff7e1880 }, { "RTL8168B/8111B", MCFG_METHOD_12, 0xff7e1880 }, { "RTL8101E", MCFG_METHOD_13, 0xff7e1880 }, { "RTL8100E", MCFG_METHOD_14, 0xff7e1880 }, { "RTL8100E", MCFG_METHOD_15, 0xff7e1880 }, { 0 }};/* Maximum events (Rx packets, etc.) to handle at each interrupt. */int RealtekR1000::max_interrupt_work = 20;/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */int RealtekR1000::multicast_filter_limit = 32;const unsigned int RealtekR1000::ethernet_polynomial = 0x04c11db7U;static const u16 r1000_intr_mask = LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK ;static const unsigned int r1000_rx_config = (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift) | 0x0000000E;/* * Initialization of driver instance, * i.e. resources allocation and so on.*/bool RealtekR1000::init(OSDictionary *properties){ DLog("RealtekR1000::init(OSDictionary *properties)\n"); if (BaseClass::init(properties) == false) return false; pciDev = NULL; mmioBase = NULL; workLoop = NULL; intSource = NULL; timerSource = NULL; netStats = NULL; etherStats = NULL; transmitQueue = NULL; netif = NULL; enabled = false; forcedPio = false; isInitialized = false; enabledForKDP = enabledForBSD = false; return true;}/* * Calling before destroing driver instance. * Frees all allocated resources.*/void RealtekR1000::free(){ DLog("RealtekR1000::free()"); //free resource of base instance if (intSource && workLoop) { //Detaching interrupt source from work loop workLoop->removeEventSource(intSource); } RELEASE(netif); RELEASE(intSource); RELEASE(timerSource); RELEASE(mmioBase); RELEASE(pciDev); RELEASE(workLoop); FreeDescriptorsMemory(); BaseClass::free();} /* * Starting driver.*/bool RealtekR1000::start(IOService *provider){ bool success = false; DLog("::start(IOService *%08x)\n", provider); do { pciDev = OSDynamicCast(IOPCIDevice, provider); if (!pciDev) { DLog("::start: Failed to cast provider\n"); break; } if (BaseClass::start(pciDev) == false) { DLog("::start: Failed super::start returned false\n"); break; } pciDev->retain(); if(pciDev->open(this) == false) { DLog("::start: Failed to open PCI Device/Nub\n"); break; } chipset = 5; //Assuming RTL8168/8111 by default //Adding Mac OS X PHY's mediumDict = OSDictionary::withCapacity(MEDIUM_INDEX_COUNT + 1); OSAddNetworkMedium(kIOMediumEthernetAuto, 0, MEDIUM_INDEX_AUTO); OSAddNetworkMedium(kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex, 10 * MBit, MEDIUM_INDEX_10HD); OSAddNetworkMedium(kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex, 10 * MBit, MEDIUM_INDEX_10FD); OSAddNetworkMedium(kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex, 100 * MBit, MEDIUM_INDEX_100HD); OSAddNetworkMedium(kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex, 100 * MBit, MEDIUM_INDEX_100FD); OSAddNetworkMedium(kIOMediumEthernet1000BaseTX | kIOMediumOptionHalfDuplex, 1000 * MBit, MEDIUM_INDEX_1000HD); OSAddNetworkMedium(kIOMediumEthernet1000BaseTX | kIOMediumOptionFullDuplex, 1000 * MBit, MEDIUM_INDEX_1000FD); if (!publishMediumDictionary(mediumDict)) { DLog("::start: Failed publishMediumDictionary returned false"); break; } if (!R1000ProbeAndStartBoard()) { DLog("::start: Failed R1000ProbeAndStartBoard returned false"); break; } if (!AllocateDescriptorsMemory()) { DLog("::start: Failed AllocateDescriptorsMemory returned false"); break; } if (!R1000InitEventSources(provider)) { DLog("::start: Failed R1000InitEventSources returned false"); break; } success = true; } while ( false ); // Close our provider, it will be re-opened on demand when // our enable() is called by a client. if(pciDev) { pciDev->close(this); } do { // break if we've had an error before this if ( false == success ) { break; } //Attaching dynamic link layer if (false == attachInterface((IONetworkInterface**)&netif, false)) { DLog("::start: Failed 'attachInterface' in attaching to data link layer\n"); break; } netif->registerService(); success = true; } while ( false ); // set isInitialized status isInitialized = true; DLog("::start: returning '%d'\n",success); return success;}/* * Stopping driver.*/void RealtekR1000::stop(IOService *provider){ DLog("RealtekR1000::stop(IOService *provider)\n"); detachInterface(netif); R1000StopBoard(); BaseClass::stop(provider);}bool RealtekR1000::OSAddNetworkMedium(ulong type, UInt32 bps, ulong index){ IONetworkMedium *medium; medium = IONetworkMedium::medium( type, bps, 0, index ); if (!medium) { DLog("Couldn't allocate medium\n"); return false; } if (!IONetworkMedium::addMedium(mediumDict, medium)) { DLog("Couldn't add medium\n"); return false; } mediumTable[index] = medium; return true;}bool RealtekR1000::increaseActivationLevel(UInt32 level){ bool ret = false; switch (level) { case kActivationLevelKDP: if (!pciDev) break; pciDev->open(this); // PHY medium selection. const IONetworkMedium *medium = getSelectedMedium(); if (!medium) { DLog("Selected medium is NULL, forcing to autonegotiation\n"); medium = mediumTable[MEDIUM_INDEX_AUTO]; } else { DLog("Selected medium index %d", medium->getIndex()); } selectMedium(medium); timerSource->setTimeoutMS(TX_TIMEOUT); ret = true; break; case kActivationLevelBSD: if (!R1000OpenAdapter()) break; transmitQueue->setCapacity(kTransmitQueueCapacity); transmitQueue->start(); ret = true; break; } return ret;}bool RealtekR1000::decreaseActivationLevel(UInt32 level){ switch (level) { case kActivationLevelKDP: timerSource->cancelTimeout(); if (pciDev) pciDev->close(this); break; case kActivationLevelBSD: transmitQueue->stop(); transmitQueue->setCapacity(0); transmitQueue->flush(); R1000CloseAdapter(); break; } return true;}bool RealtekR1000::setActivationLevel(UInt32 level){ bool success = false; DLog("setActivationLevel %d\n", level); if (activationLevel == level) return true; for ( ; activationLevel > level; activationLevel--) { if ((success = decreaseActivationLevel(activationLevel)) == false) break; } for ( ; activationLevel < level; activationLevel++ ) { if ((success = increaseActivationLevel(activationLevel+1)) == false) break; } return success;}/* * A request from an interface client to enable the controller.*/IOReturn RealtekR1000::enable(IONetworkInterface *netif){ DLog("RealtekR1000::enable(IONetworkInterface *netif)\n"); if (enabledForBSD) return kIOReturnSuccess; enabledForBSD = setActivationLevel(kActivationLevelBSD); if (enabledForBSD) { this->enabled = true; return kIOReturnSuccess; } else return kIOReturnIOError;}/* * A request from an interface client to disable the controller.*/IOReturn RealtekR1000::disable(IONetworkInterface *netif){ enabledForBSD = false; setActivationLevel(enabledForKDP ? kActivationLevelKDP : kActivationLevelNone); this->enabled = false; return kIOReturnSuccess;}/* * Transmits an output packet. * packet - an mbuf chain containing the output packet to be sent on the network. * param - a parameter provided by the caller.*/UInt32 RealtekR1000::outputPacket(mbuf_t m, void *param){ //DLog("RedaltekR1000::outputPacket(mbuf_t m, void *param)\n"); int entry = cur_tx % NUM_TX_DESC; int buf_len = 60; if ((OSSwapLittleToHostInt32(TxDescArray[entry].status) & OWNbit) == 0) { if (mbuf_pkthdr_len(m) <= tx_pkt_len) buf_len = mbuf_pkthdr_len(m); else { DLog("Tx Packet size is too big, droping\n"); freePacket(m); return kIOReturnOutputDropped; } Tx_skbuff[entry] = m; TxDescArray[entry].buf_addr = OSSwapHostToLittleInt32(Tx_skbuff_Dma[entry]); uchar *data_ptr = Tx_dbuff[entry]; ulong pkt_snd_len = 0; mbuf_t cur_buf = m; do { if (mbuf_data(cur_buf)) bcopy(mbuf_data(cur_buf), data_ptr, mbuf_len(cur_buf)); data_ptr += mbuf_len(cur_buf); pkt_snd_len += mbuf_len(cur_buf); } while(((cur_buf = mbuf_next(cur_buf)) != NULL) && ((pkt_snd_len + mbuf_len(cur_buf)) <= buf_len)); buf_len = pkt_snd_len; if (entry != (NUM_TX_DESC - 1)) { TxDescArray[entry].status = OSSwapHostToLittleInt32((OWNbit | FSbit | LSbit) | buf_len); } else { TxDescArray[entry].status = OSSwapHostToLittleInt32((OWNbit | EORbit | FSbit | LSbit) | buf_len); } WriteMMIO8 ( TxPoll, 0x40); //set polling bit cur_tx++; //DLog("mbuf_len %d, packet_len %d\n", mbuf_len(m), mbuf_pkthdr_len(m)); //DLog("cur_tx %d, dirty_tx %d, buf_len %d\n", cur_tx, dirty_tx, buf_len, mbuf_data(m), mbuf_data_to_physical(mbuf_data(m))); } else { DLog("TX_RING_IS_FULL stalling\n"); return kIOReturnOutputStall; } /*if ((cur_tx - NUM_TX_DESC) == dirty_tx) { transmitQueue->stop(); } else { transmitQueue->start(); }*/ return kIOReturnOutputSuccess;}void RealtekR1000::getPacketBufferConstraints(IOPacketBufferConstraints *constraints) const{ DLog("RealtekR1000::getPacketBufferConstraints(IOPacketBufferConstraints *constraints) const\n"); constraints->alignStart = kIOPacketBufferAlign4; constraints->alignLength = kIOPacketBufferAlign4;}IOOutputQueue *RealtekR1000::createOutputQueue(){ DLog("RealtekR1000::createOutputQueue()\n"); //Sharing one event source with transmith/receive handles return IOGatedOutputQueue::withTarget(this, getWorkLoop());}/* * Returns a string describing the vendor of the network controller. The caller is responsible for releasing the string object returned.*/const OSString *RealtekR1000::newVendorString() const{ DLog("RealtekR1000::newVendorString() const\n"); return OSString::withCString("Realtek");}/* * Returns a string describing the model of the network controller. The caller is responsible for releasing the string object returned.*/const OSString *RealtekR1000::newModelString() const{ DLog("RealtekR1000::newModelString() const\n"); return OSString::withCString(rtl_chip_info[chipset].name);}/* * A client request to change the medium selection. * This method is called when a client issues a command for the controller to change its * current medium selection. The implementation must call setSelectedMedium() after the change * has occurred. This method call is synchronized by the workloop's gate.*/IOReturn RealtekR1000::selectMedium(const IONetworkMedium *medium){ DLog("RealtekR1000::selectMedium(const IONetworkMedium *medium)\n"); DLog("index %d\n", medium->getIndex()); if (medium) { switch (medium->getIndex()) { case MEDIUM_INDEX_AUTO: R1000SetMedium(SPEED_100, DUPLEX_FULL, AUTONEG_ENABLE); break; case MEDIUM_INDEX_10HD: R1000SetMedium(SPEED_10, DUPLEX_HALF, AUTONEG_DISABLE); break; case MEDIUM_INDEX_10FD: R1000SetMedium(SPEED_10, DUPLEX_FULL, AUTONEG_DISABLE); break; case MEDIUM_INDEX_100HD: R1000SetMedium(SPEED_100, DUPLEX_HALF, AUTONEG_DISABLE); break; case MEDIUM_INDEX_100FD: R1000SetMedium(SPEED_100, DUPLEX_FULL, AUTONEG_DISABLE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -