diff --git a/src/add-ons/kernel/drivers/network/Jamfile b/src/add-ons/kernel/drivers/network/Jamfile index 88f48f92e8..5dadc90165 100644 --- a/src/add-ons/kernel/drivers/network/Jamfile +++ b/src/add-ons/kernel/drivers/network/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel drivers network ; SubInclude HAIKU_TOP src add-ons kernel drivers network etherpci ; SubInclude HAIKU_TOP src add-ons kernel drivers network pegasus ; SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8169 ; +SubInclude HAIKU_TOP src add-ons kernel drivers network sis19x ; SubInclude HAIKU_TOP src add-ons kernel drivers network sis900 ; SubInclude HAIKU_TOP src add-ons kernel drivers network usb_asix ; SubInclude HAIKU_TOP src add-ons kernel drivers network usb_davicom ; diff --git a/src/add-ons/kernel/drivers/network/sis19x/DataRing.cpp b/src/add-ons/kernel/drivers/network/sis19x/DataRing.cpp new file mode 100644 index 0000000000..51150d8a38 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/DataRing.cpp @@ -0,0 +1,359 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + +#include "DataRing.h" + +#include + +#include "Driver.h" +#include "Settings.h" +#include "Device.h" + + +// +// Tx stuff implementation +// + +template<> +void +DataRing::_SetBaseAddress(phys_addr_t address) +{ + fDevice->WritePCI32(TxBase, (uint32)address); +} + + +template<> +status_t +DataRing::Write(const uint8* buffer, + size_t* numBytes) +{ + *numBytes = min_c(*numBytes, MaxFrameSize); + + // wait for available tx descriptor + status_t status = acquire_sem_etc(fSemaphore, 1, B_TIMEOUT, TransmitTimeout); + if (status < B_NO_ERROR) { + TRACE_ALWAYS("Cannot acquire sem:%#010x\n", status); + return status; + } + + cpu_status cpuStatus = disable_interrupts(); + acquire_spinlock(&fSpinlock); + + uint32 index = fHead % TxDescriptorsCount; + volatile TxDescriptor& Descriptor = fDescriptors[index]; + + // check if the buffer not owned by hardware + uint32 descriptorStatus = Descriptor.fCommandStatus; + if ((descriptorStatus & TDC_TXOWN) == 0) { + + // copy data into buffer + status = user_memcpy((void*)fBuffers[index], buffer, *numBytes); + + // take care about tx descriptor + Descriptor.fPacketSize = *numBytes; + Descriptor.fEOD |= *numBytes; + Descriptor.fCommandStatus = TDC_PADEN | TDC_CRCEN + | TDC_DEFEN | TDC_THOL3 | TDC_TXINT; + if ((fDevice->LinkState().media & IFM_HALF_DUPLEX) != 0) { + Descriptor.fCommandStatus |= TDC_BKFEN | TDC_CRSEN | TDC_COLSEN; + if (fDevice->LinkState().speed == 1000000) { + Descriptor.fCommandStatus |= TDC_BSTEN | TDC_EXTEN; + } + } + + Descriptor.fCommandStatus |= TDC_TXOWN; + fHead++; + } + + fDevice->WritePCI32(TxControl, fDevice->ReadPCI32(TxControl) | TxControlPoll); + + release_spinlock(&fSpinlock); + restore_interrupts(cpuStatus); + + // if buffer was owned by hardware - notify about it + if ((descriptorStatus & TDC_TXOWN) != 0) { + release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE); + TRACE_ALWAYS("Buffer is still owned by the card.\n"); + status = B_BUSY; + } + + // TRACE_ALWAYS("Write:%d bytes:%#010x!\n", *numBytes, status); + + return status; +} + + +template<> +int32 +DataRing::InterruptHandler() +{ + uint32 releasedFrames = 0; + + acquire_spinlock(&fSpinlock); + + while (fTail != fHead) { + + uint32 index = fTail % TxDescriptorsCount; + volatile TxDescriptor& Descriptor = fDescriptors[index]; + uint32 status = Descriptor.fCommandStatus; + +#if STATISTICS + fDevice->fStatistics.PutTxStatus(status, Descriptor.fEOD/*PacketSize*/); +#endif + + /*if (status & TDC_TXOWN) { + //fDevice->WritePCI32(TxControl, fDevice->ReadPCI32(TxControl) | TxControlPoll); + break; //still owned by hardware - poll again ... + }*/ + + Descriptor.fPacketSize = 0; + Descriptor.fCommandStatus = 0; + Descriptor.fEOD &= TxDescriptorEOD; + + releasedFrames++; + + fTail++; + } + + release_spinlock(&fSpinlock); + + if (releasedFrames > 0) { + release_sem_etc(fSemaphore, releasedFrames, B_DO_NOT_RESCHEDULE); + return B_INVOKE_SCHEDULER; + } + + return B_HANDLED_INTERRUPT; +} + + +template<> +void +DataRing::CleanUp() +{ + cpu_status cpuStatus = disable_interrupts(); + acquire_spinlock(&fSpinlock); + + fDevice->WritePCI32(IntMask, 0 ); + + uint32 txControl = fDevice->ReadPCI32(TxControl); + txControl &= ~(TxControlPoll | TxControlEnable); + fDevice->WritePCI32(TxControl, txControl); + + spin(50); + + uint32 droppedFrames = fHead - fTail; + /* + for (;fHead != fTail; fHead--, droppedFrames++) { + uint32 index = fHead % TxDescriptorsCount; + volatile TxDescriptor& Descriptor = fDescriptors[index]; + + / * if (Descriptor.fCommandStatus & TDC_TXOWN) { + continue; //still owned by hardware - ignore? + }* / + + Descriptor.fPacketSize = 0; + Descriptor.fCommandStatus = 0; + Descriptor.fEOD &= TxDescriptorEOD; + } + */ +#if STATISTICS + fDevice->fStatistics.fDropped += droppedFrames; +#endif + + fHead = fTail = 0; + + for (size_t i = 0; i < TxDescriptorsCount; i++) { + fDescriptors[i].fPacketSize = 0; + fDescriptors[i].fCommandStatus = 0; + fDescriptors[i].fEOD &= TxDescriptorEOD;; + } + + // uint32 txBase = fDevice->ReadPCI32(TxBase); + //uint32 index = fHead % TxDescriptorsCount; + //fDevice->WritePCI32(TxStatus, txBase + 8 /*+ index * sizeof(TxDescriptor)*/); + //fDevice->WritePCI32(TxBase, txBase); + + if (droppedFrames > 0) { + release_sem_etc(fSemaphore, droppedFrames, B_DO_NOT_RESCHEDULE); + } + + txControl |= TxControlEnable; + fDevice->WritePCI32(TxControl, txControl); + + fDevice->WritePCI32(IntMask, knownInterruptsMask); + + release_spinlock(&fSpinlock); + restore_interrupts(cpuStatus); +} + + +template<> +void +DataRing::Dump() +{ + int32 count = 0; + get_sem_count(fSemaphore, &count); + kprintf("Tx:[count:%ld] head:%lu tail:%lu dirty:%lu\n", + count, fHead, fTail, fHead - fTail); + + kprintf("\tPktSize\t\tCmdStat\t\tBufPtr\t\tEOD\n"); + + for (size_t i = 0; i < TxDescriptorsCount; i++) { + volatile TxDescriptor& D = fDescriptors[i]; + char marker = ((fTail % TxDescriptorsCount) == i) ? '=' : ' '; + marker = ((fHead % TxDescriptorsCount) == i) ? '>' : marker; + kprintf("%02lx %c\t%08lx\t%08lx\t%08lx\t%08lx\n", i, marker, + D.fPacketSize, D.fCommandStatus, D.fBufferPointer, D.fEOD); + } +} + + +// +// Rx stuff implementation +// + +template<> +void +DataRing::_SetBaseAddress(phys_addr_t address) +{ + fDevice->WritePCI32(RxBase, (uint32)address); +} + + +template<> +int32 +DataRing::InterruptHandler() +{ + uint32 receivedFrames = 0; + + acquire_spinlock(&fSpinlock); + + uint32 index = fHead % RxDescriptorsCount; + uint32 status = fDescriptors[index].fStatusSize; + uint32 info = fDescriptors[index].fPacketInfo; + + while (((info & RDI_RXOWN) == 0) && (fHead - fTail) <= RxDescriptorsCount) { + +#if STATISTICS + fDevice->fStatistics.PutRxStatus(status); +#endif + receivedFrames++; + + fHead++; + + index = fHead % RxDescriptorsCount; + status = fDescriptors[index].fStatusSize; + info = fDescriptors[index].fPacketInfo; + } + + release_spinlock(&fSpinlock); + + if (receivedFrames > 0) { + release_sem_etc(fSemaphore, receivedFrames, B_DO_NOT_RESCHEDULE); + return B_INVOKE_SCHEDULER; + } + + return B_UNHANDLED_INTERRUPT; //XXX: ???? +} + + +template<> +status_t +DataRing::Read(uint8* buffer, size_t* numBytes) +{ + status_t rstatus = B_ERROR; + + do { + // wait for received rx descriptor + uint32 flags = B_CAN_INTERRUPT | fDevice->fBlockFlag; + status_t acquireStatus = acquire_sem_etc(fSemaphore, 1, flags, 0); + if (acquireStatus != B_NO_ERROR) { + TRACE_ALWAYS("Cannot acquire sem:%#010x\n", acquireStatus); + return acquireStatus; + } + + cpu_status cpuStatus = disable_interrupts(); + acquire_spinlock(&fSpinlock); + + uint32 index = fTail % RxDescriptorsCount; + volatile RxDescriptor& Descriptor = fDescriptors[index]; + + // check if the buffer owned by hardware - should never occure! + uint32 status = Descriptor.fStatusSize; + uint32 info = Descriptor.fPacketInfo; + uint16 count = (status & 0x7f000000) >> 24; + bool isFrameValid = false; + //status_t rstatus = B_ERROR; + + if ((info & RDI_RXOWN) == 0) { + isFrameValid = (status & rxErrorStatusBits) == 0 && (status & RDS_CRCOK) != 0; + if (isFrameValid) { + // frame is OK - copy it into buffer + *numBytes = status & RDS_SIZE; + rstatus = user_memcpy(buffer, (void*)fBuffers[index], *numBytes); + } + } + + // take care about rx descriptor + Descriptor.fStatusSize = 0; + Descriptor.fPacketInfo = RDI_RXOWN | RDI_RXINT; + + fTail++; + + release_spinlock(&fSpinlock); + restore_interrupts(cpuStatus); + + if ((info & RDI_RXOWN) != 0) { + TRACE_ALWAYS("Buffer is still owned by the card.\n"); + } else { + if (!isFrameValid) { + TRACE_ALWAYS("Invalid frame received, status:%#010x;info:%#010x!\n", status, info); + } /*else { + TRACE_ALWAYS("Read:%d bytes;st:%#010x;info:%#010x!\n", *numBytes, status, info); + } */ + // we have free rx buffer - reenable potentially idle state machine + fDevice->WritePCI32(RxControl, fDevice->ReadPCI32(RxControl) | RxControlPoll | RxControlEnable); + } + + if (count > 1) { + TRACE_ALWAYS("Warning:Descriptors count is %d!\n", count); + } + + } while (rstatus != B_OK); + + return rstatus; +} + + +template<> +void +DataRing::Dump() +{ + int32 count = 0; + get_sem_count(fSemaphore, &count); + kprintf("Rx:[count:%ld] head:%lu tail:%lu dirty:%lu\n", + count, fHead, fTail, fHead - fTail); + + for (size_t i = 0; i < 2; i++) { + kprintf("\tStatSize\tPktInfo\t\tBufPtr\t\tEOD %c", + i == 0 ? '|' : '\n'); + } + + for (size_t i = 0; i < RxDescriptorsCount / 2; i++) { + const char* mask = "%02lx %c\t%08lx\t%08lx\t%08lx\t%08lx %c"; + + for (size_t ii = 0; ii < 2; ii++) { + size_t index = ii == 0 ? i : (i + RxDescriptorsCount / 2); + volatile RxDescriptor& D = fDescriptors[index]; + char marker = ((fTail % RxDescriptorsCount) == index) ? '=' : ' '; + marker = ((fHead % RxDescriptorsCount) == index) ? '>' : marker; + kprintf(mask, index, marker, D.fStatusSize, D.fPacketInfo, + D.fBufferPointer, D.fEOD, ii == 0 ? '|' : '\n' ); + } + } +} + diff --git a/src/add-ons/kernel/drivers/network/sis19x/DataRing.h b/src/add-ons/kernel/drivers/network/sis19x/DataRing.h new file mode 100644 index 0000000000..af4ebfb625 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/DataRing.h @@ -0,0 +1,174 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _SiS19X_DATARING_H_ +#define _SiS19X_DATARING_H_ + + +#include + +#include "Driver.h" +#include "Registers.h" +#include "Settings.h" + + +class Device; + +template +class DataRing { +public: + DataRing(Device* device, bool isTx); + ~DataRing(); + + status_t Open(); + void CleanUp(); + status_t Close(); + + status_t Read(uint8* buffer, size_t* numBytes); + status_t Write(const uint8* buffer, size_t* numBytes); + + int32 InterruptHandler(); + + void Trace(); + void Dump(); + +private: + status_t _InitArea(); + void _SetBaseAddress(phys_addr_t address); + + Device* fDevice; + bool fIsTx; + status_t fStatus; + area_id fArea; + int32 fSpinlock; + sem_id fSemaphore; + uint32 fHead; + uint32 fTail; + + volatile __type* fDescriptors; + volatile uint8* fBuffers[__count]; +}; + + + +template +DataRing<__type, __count>::DataRing(Device* device, bool isTx) + : + fDevice(device), + fIsTx(isTx), + fStatus(B_NO_INIT), + fArea(-1), + fSpinlock(0), + fSemaphore(0), + fHead(0), + fTail(0), + fDescriptors(NULL) +{ + memset(fBuffers, 0, sizeof(fBuffers)); +} + + +template +DataRing<__type, __count>::~DataRing() +{ + delete_sem(fSemaphore); + delete_area(fArea); +} + + +template +status_t +DataRing<__type, __count>::_InitArea() +{ + // create area for xfer data descriptors and buffers... + // + // layout is following: + // | descriptors array | buffers array | + // + uint32 buffSize = BufferSize + sizeof(__type); + buffSize *= __count; + buffSize = (buffSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); + fArea = create_area(DRIVER_NAME "_data_ring", (void**)&fDescriptors, + B_ANY_KERNEL_ADDRESS, buffSize, + B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); + if (fArea < 0) { + TRACE_ALWAYS("Cannot create area with size %d bytes:%#010x\n", + buffSize, fArea); + return fStatus = fArea; + } + + // setup descriptors and buffers layout + uint8* buffersData = (uint8*)fDescriptors; + uint32 descriptorsSize = sizeof(__type) * __count; + buffersData += descriptorsSize; + + physical_entry table = {0}; + + for (size_t i = 0; i < __count; i++) { + fBuffers[i] = buffersData + BufferSize * i; + + get_memory_map((void*)fBuffers[i], BufferSize, &table, 1); + fDescriptors[i].Init(table.address, i == (__count - 1)); + } + + get_memory_map((void*)fDescriptors, descriptorsSize, &table, 1); + + _SetBaseAddress(table.address); + + return fStatus = B_OK; +} + + +template +status_t +DataRing<__type, __count>::Open() +{ + if (fStatus != B_OK && _InitArea() != B_OK) { + return fStatus; + } + + if (fIsTx) { + fSemaphore = create_sem(__count, "SiS19X Transmit"); + } else { + fSemaphore = create_sem(0, "SiS19X Receive"); + } + + if (fSemaphore < 0) { + TRACE_ALWAYS("Cannot create %s semaphore:%#010x\n", + fIsTx ? "transmit" : "receive", fSemaphore); + return fStatus = fSemaphore; + } + + set_sem_owner(fSemaphore, B_SYSTEM_TEAM); + + return fStatus = B_OK; +} + + +template +status_t +DataRing<__type, __count>::Close() +{ + delete_sem(fSemaphore); + fSemaphore = 0; + + return B_OK; +} + + +template +void +DataRing<__type, __count>::Trace() +{ + int32 count = 0; + get_sem_count(fSemaphore, &count); + TRACE_ALWAYS("%s:[count:%d] n:%lu l:%lu d:%lu\n", fIsTx ? "Tx" : "Rx", + count, fHead, fTail, fHead - fTail); +} + + +#endif //_SiS19X_DATARING_H_ + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Device.cpp b/src/add-ons/kernel/drivers/network/sis19x/Device.cpp new file mode 100644 index 0000000000..e33c075549 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Device.cpp @@ -0,0 +1,676 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + + +#include "Device.h" + +#include +#include + +#include "Driver.h" +#include "Settings.h" +#include "Registers.h" + + +Device::Device(Device::Info &DeviceInfo, pci_info &PCIInfo) + : + fStatus(B_ERROR), + fPCIInfo(PCIInfo), + fInfo(DeviceInfo), + fIOBase(0), + fHWSpinlock(0), + fInterruptsNest(0), + fFrameSize(MaxFrameSize), + fMII(this), + fOpen(false), + fBlockFlag(0), + fLinkStateChangeSem(-1), + fHasConnection(false), + fTxDataRing(this, true), + fRxDataRing(this, false) +{ + memset((struct timer*)this, 0, sizeof(struct timer)); + + uint32 cmdRegister = gPCIModule->read_pci_config(PCIInfo.bus, + PCIInfo.device, PCIInfo.function, PCI_command, 2); + TRACE_ALWAYS("cmdRegister:%#010x\n", cmdRegister); + cmdRegister |= PCI_command_io | PCI_command_memory | PCI_command_master; + gPCIModule->write_pci_config(PCIInfo.bus, PCIInfo.device, + PCIInfo.function, PCI_command, 2, cmdRegister); + + fIOBase = PCIInfo.u.h0.base_registers[1]; + TRACE_ALWAYS("fIOBase:%#010x\n", fIOBase); + + fStatus = B_OK; +} + + +Device::~Device() +{ +} + + +status_t +Device::Open(uint32 flags) +{ + TRACE("flags:%x\n", flags); + if (fOpen) { + TRACE_ALWAYS("An attempt to re-open device ignored.\n"); + return B_BUSY; + } + + status_t result = fMII.Init(); + if (result != B_OK) { + TRACE_ALWAYS("MII initialization failed: %#010x.\n", result); + return result; + } + + _Reset(); + + if ((fMII.LinkState().media & IFM_ACTIVE) == 0/*fNegotiationComplete*/) { + fMII.UpdateLinkState(); + } + + fMII.SetMedia(); + + WritePCI32(RxMACAddress, 0); + _InitRxFilter(); + + fRxDataRing.Open(); + fTxDataRing.Open(); + + if (atomic_add(&fInterruptsNest, 1) == 0) { + install_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line, + InterruptHandler, this, 0); + TRACE("Interrupt handler installed at line %d.\n", + fPCIInfo.u.h0.interrupt_line); + } + + _SetRxMode(false); + + // enable al known interrupts + WritePCI32(IntMask, knownInterruptsMask); + + // enable Rx and Tx + uint32 control = ReadPCI32(RxControl); + control |= RxControlEnable | RxControlPoll; + WritePCI32(RxControl, control); + + control = ReadPCI32(TxControl); + control |= TxControlEnable /*| TxControlPoll*/; + WritePCI32(TxControl, control); + + add_timer((timer*)this, _TimerHandler, 1000000LL, B_PERIODIC_TIMER); + + //fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK; + fOpen = true; + return B_OK; +} + + +status_t +Device::Close() +{ + TRACE("closed!\n"); + + // disable interrupts + WritePCI32(IntMask, 0); + spin(2000); + + // Stop Tx / Rx status machine + uint32 status = ReadPCI32(IntControl); + status |= 0x00008000; + WritePCI32(IntControl, status); + spin(50); + status &= ~0x00008000; + WritePCI32(IntControl, status); + + if (atomic_add(&fInterruptsNest, -1) == 1) { + remove_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line, + InterruptHandler, this); + TRACE("Interrupt handler at line %d uninstalled.\n", + fPCIInfo.u.h0.interrupt_line); + } + + fRxDataRing.Close(); + fTxDataRing.Close(); + + cancel_timer((timer*)this); + + TRACE("timer cancelled\n"); + + fOpen = false; + + return B_OK; +} + + +status_t +Device::Free() +{ + // fRxDataRing.Free(); + // fTxDataRing.Free(); + + TRACE("freed\n"); + return B_OK; +} + + +status_t +Device::Read(uint8 *buffer, size_t *numBytes) +{ + return fRxDataRing.Read(buffer, numBytes); +} + + +status_t +Device::Write(const uint8 *buffer, size_t *numBytes) +{ + if ((fMII.LinkState().media & IFM_ACTIVE) == 0) { + TRACE_ALWAYS("Write failed. link is inactive!\n"); + return B_OK; // return OK because of well-known DHCP "moustreap"! + } + + return fTxDataRing.Write(buffer, numBytes); +} + + +status_t +Device::Control(uint32 op, void *buffer, size_t length) +{ + switch (op) { + case ETHER_INIT: + TRACE("ETHER_INIT\n"); + return B_OK; + + case ETHER_GETADDR: + memcpy(buffer, &fMACAddress, sizeof(fMACAddress)); + TRACE("ETHER_GETADDR %#02x:%#02x:%#02x:%#02x:%#02x:%#02x\n", + fMACAddress.ebyte[0], fMACAddress.ebyte[1], + fMACAddress.ebyte[2], fMACAddress.ebyte[3], + fMACAddress.ebyte[4], fMACAddress.ebyte[5]); + return B_OK; + + case ETHER_GETFRAMESIZE: + *(uint32 *)buffer = fFrameSize; + TRACE("ETHER_ETHER_GETFRAMESIZE:%d\n",fFrameSize); + return B_OK; + + case ETHER_NONBLOCK: + TRACE("ETHER_NONBLOCK\n"); + fBlockFlag = *((uint32*)buffer) ? B_TIMEOUT : 0; + return B_OK; + + case ETHER_SETPROMISC: + TRACE("ETHER_SETPROMISC\n"); + return _SetRxMode(*((uint8*)buffer)); + + case ETHER_ADDMULTI: + case ETHER_REMMULTI: + TRACE_ALWAYS("Multicast operations are not implemented.\n"); + return B_ERROR; + + case ETHER_SET_LINK_STATE_SEM: + fLinkStateChangeSem = *(sem_id *)buffer; + TRACE_ALWAYS("ETHER_SET_LINK_STATE_SEM\n"); + return B_OK; + + case ETHER_GET_LINK_STATE: + return GetLinkState((ether_link_state *)buffer); + + default: + TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op); + } + + return B_DEV_INVALID_IOCTL; +} + + +status_t +Device::SetupDevice() +{ + ether_address address; + status_t result = ReadMACAddress(address); + if (result != B_OK) { + TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result); + return result; + } + + TRACE("MAC address is:%02x:%02x:%02x:%02x:%02x:%02x\n", + address.ebyte[0], address.ebyte[1], address.ebyte[2], + address.ebyte[3], address.ebyte[4], address.ebyte[5]); + + fMACAddress = address; + + uint16 info = _ReadEEPROM(EEPROMInfo); + fMII.SetRGMII((info & 0x0080) != 0); + + TRACE("RGMII is '%s'. EEPROM info word:%#06x.\n", + fMII.HasRGMII() ? "on" : "off", info); + + fMII.SetGigagbitCapable(fInfo.Id() == SiS191); + + return B_OK; +} + + +void +Device::TeardownDevice() +{ + +} + + +uint8 +Device::ReadPCI8(int offset) +{ + return gPCIModule->read_io_8(fIOBase + offset); +} + + +uint16 +Device::ReadPCI16(int offset) +{ + return gPCIModule->read_io_16(fIOBase + offset); +} + + +uint32 +Device::ReadPCI32(int offset) +{ + return gPCIModule->read_io_32(fIOBase + offset); +} + + +void +Device::WritePCI8(int offset, uint8 value) +{ + gPCIModule->write_io_8(fIOBase + offset, value); +} + + +void +Device::WritePCI16(int offset, uint16 value) +{ + gPCIModule->write_io_16(fIOBase + offset, value); +} + + +void +Device::WritePCI32(int offset, uint32 value) +{ + gPCIModule->write_io_32(fIOBase + offset, value); +} + +/* + cpu_status + Device::Lock() + { + cpu_status st = disable_interrupts(); + acquire_spinlock(&fHWSpinlock); + return st; + } + + + void + Device::Unlock(cpu_status st) + { + release_spinlock(&fHWSpinlock); + restore_interrupts(st); + } + */ + +int32 +Device::InterruptHandler(void *InterruptParam) +{ + Device *device = (Device*)InterruptParam; + if(device == 0) { + TRACE_ALWAYS("Invalid parameter in the interrupt handler.\n"); + return B_HANDLED_INTERRUPT; + } + + int32 result = B_UNHANDLED_INTERRUPT; + + acquire_spinlock(&device->fHWSpinlock); + + // disable interrupts... + device->WritePCI32(IntMask, 0); + + //int maxWorks = 40; + + //do { + uint32 status = device->ReadPCI32(IntSource); + +#if STATISTICS + device->fStatistics.PutStatus(status); +#endif + device->WritePCI32(IntSource, status); + + if ((status & knownInterruptsMask) != 0) { + //break; + //} + + // XXX: ???? + result = B_HANDLED_INTERRUPT; + + if ((status & (/*INT_TXIDLE |*/ INT_TXDONE)) != 0 ) { + result = device->fTxDataRing.InterruptHandler(); + } + + if ((status & (/*INT_RXIDLE |*/ INT_RXDONE)) != 0 ) { + result = device->fRxDataRing.InterruptHandler(); + } + + /*if ((status & (INT_LINK)) != 0 ) { + //if (!device->fMII.isLinkUp()) { + device->fTxDataRing.CleanUp(); + //} + }*/ + } + + //} while (--maxWorks > 0); + + // enable interrupts... + device->WritePCI32(IntMask, knownInterruptsMask); + + release_spinlock(&device->fHWSpinlock); + + return result; +} + + +status_t +Device::GetLinkState(ether_link_state *linkState) +{ + status_t result = user_memcpy(linkState, &fMII.LinkState(), + sizeof(ether_link_state)); + +#if STATISTICS + fStatistics.Trace(); + fRxDataRing.Trace(); + fTxDataRing.Trace(); + uint32 rxControl = ReadPCI32(RxControl); + uint32 txControl = ReadPCI32(TxControl); + TRACE_ALWAYS("RxControl:%#010x;TxControl:%#010x\n", rxControl, txControl); +#endif + + TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n", + (linkState->media & IFM_ACTIVE) ? "active" : "inactive", + linkState->speed / 1000, + (linkState->media & IFM_FULL_DUPLEX) ? "full" : "half"); + + return result; +} + + +status_t +Device::_SetRxMode(bool isPromiscuousModeOn) +{ + // clean the Rx MAC Control register + WritePCI16(RxMACControl, (ReadPCI16(RxMACControl) & ~RXM_Mask)); + + uint16 rxMode = RXM_Broadcast | RXM_Multicast | RXM_Physical; + if (isPromiscuousModeOn) { + rxMode |= RXM_AllPhysical; + } + + // set multicast filters + WritePCI32(RxHashTable, 0xffffffff); + WritePCI32(RxHashTable + 4, 0xffffffff); + + // update rx mode + WritePCI16(RxMACControl, ReadPCI16(RxMACControl) | rxMode); + + return B_OK; +} + + +int32 +Device::_TimerHandler(struct timer* timer) +{ + Device* device = (Device*)timer; + + bool linkChanged = false; + int32 result = device->fMII.TimerHandler(&linkChanged); + + if (linkChanged) { + if (device->fMII.IsLinkUp()) { + device->fTxDataRing.CleanUp(); + //device->WritePCI32(IntControl, 0x8000); + //device->ReadPCI32(IntControl); + //spin(100); + //device->WritePCI32(IntControl, 0x0); + } + } + + if (linkChanged && device->fLinkStateChangeSem > B_OK) { + release_sem_etc(device->fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); + } + + return result; +} + + +status_t +Device::_Reset() +{ + // disable interrupts + WritePCI32(IntMask, 0); + WritePCI32(IntSource, 0xffffffff); + + // reset Rx & Tx + WritePCI32(TxControl, 0x00001c00); + WritePCI32(RxControl, 0x001e1c00); + + WritePCI32(IntControl, 0x8000); + ReadPCI32(IntControl); + spin(100); + WritePCI32(IntControl, 0x0); + + WritePCI32(IntMask, 0); + WritePCI32(IntSource, 0xffffffff); + + // initial values for all MAC registers + WritePCI32(TxBase, 0x0); + WritePCI32(TxReserved, 0x0); + WritePCI32(RxBase, 0x0); + WritePCI32(RxReserved, 0x0); + + WritePCI32(PowControl, 0xffc00000); + WritePCI32(Reserved0, 0x0); + + WritePCI32(StationControl, fMII.HasRGMII() ? 0x04008001 : 0x04000001); + WritePCI32(GIoCR, 0x0); + WritePCI32(GIoControl, 0x0); + + WritePCI32(TxMACControl, 0x00002364); + WritePCI32(TxLimit, 0x0000000f); + + WritePCI32(RGDelay, 0x0); + WritePCI32(Reserved1, 0x0); + WritePCI32(RxMACControl, 0x00000252); + + WritePCI32(RxHashTable, 0x0); + WritePCI32(RxHashTable + 4, 0x0); + + WritePCI32(RxWOLControl, 0x80ff0000); + WritePCI32(RxWOLData, 0x80ff0000); + WritePCI32(RxMPSControl, 0x0); + WritePCI32(Reserved2, 0x0); + + return B_OK; +} + + +void +Device::_InitRxFilter() +{ + // store filter value + uint16 filter = ReadPCI16(RxMACControl); + + // disable disable packet filtering before address is set + WritePCI32(RxMACControl, (filter & ~RXM_Mask)); + + for (size_t i = 0; i < _countof(fMACAddress.ebyte); i++) { + WritePCI8(RxMACAddress + i, fMACAddress.ebyte[i]); + } + + // enable packet filtering + WritePCI16(RxMACControl, filter); +} + + +uint16 +Device::_ReadEEPROM(uint32 address) +{ + if (address > EIOffset) { + TRACE_ALWAYS("EEPROM address %#08x is invalid.\n", address); + return EIInvalid; + } + + WritePCI32(EEPROMInterface, EIReq | EIOpRead | (address << EIOffsetShift)); + + spin(500); // 500 ms? + + for (size_t i = 0; i < 1000; i++) { + uint32 data = ReadPCI32(EEPROMInterface); + if ((data & EIReq) == 0) { + return (data & EIData) >> EIDataShift; + } + spin(100); // 100 ms? + } + + TRACE_ALWAYS("timeout reading EEPROM.\n"); + + return EIInvalid; +} + + +status_t +Device::ReadMACAddress(ether_address_t& address) +{ + uint16 signature = _ReadEEPROM(EEPROMSignature); + TRACE("EEPROM Signature: %#06x\n", signature); + + if (signature != 0x0000 && signature != EIInvalid) { + for (size_t i = 0; i < _countof(address.ebyte) / 2; i++) { + uint16 addr = _ReadEEPROM(EEPROMAddress + i); + address.ebyte[i * 2 + 0] = (uint8)addr; + address.ebyte[i * 2 + 1] = (uint8)(addr >> 8); + } + + return B_OK; + } + + // SiS96x can use APC CMOS RAM to store MAC address, + // this is accessed through ISA bridge. + uint32 register73 = gPCIModule->read_pci_config(fPCIInfo.bus, + fPCIInfo.device, fPCIInfo.function, 0x73, 1); + TRACE_ALWAYS("Config register x73:%#010x\n", register73); + + if ((register73 & 0x00000001) == 0) + return B_ERROR; + + // look for PCI-ISA bridge + uint16 ids[] = { 0x0965, 0x0966, 0x0968 }; + + pci_info pciInfo = {0}; + for (long i = 0; B_OK == (*gPCIModule->get_nth_pci_info)(i, &pciInfo); i++) { + if (pciInfo.vendor_id != 0x1039) + continue; + + for (size_t idx = 0; idx < _countof(ids); idx++) { + if (pciInfo.device_id == ids[idx]) { + + // enable ports 0x78 0x79 to access APC registers + uint32 reg = gPCIModule->read_pci_config(pciInfo.bus, + pciInfo.device, pciInfo.function, 0x48, 1); + reg &= ~0x02; + gPCIModule->write_pci_config(pciInfo.bus, + pciInfo.device, pciInfo.function, 0x48, 1, reg); + snooze(50); + reg = gPCIModule->read_pci_config(pciInfo.bus, + pciInfo.device, pciInfo.function, 0x48, 1); + + // read factory MAC address + for (size_t i = 0; i < _countof(address.ebyte); i++) { + gPCIModule->write_io_8(0x78, 0x09 + i); + address.ebyte[i] = gPCIModule->read_io_8(0x79); + } + + // check MII/RGMII + gPCIModule->write_io_8(0x78, 0x12); + uint8 u8 = gPCIModule->read_io_8(0x79); + // TODO: set RGMII in fMII correctly! + // bool bRGMII = (u8 & 0x80) != 0; + TRACE_ALWAYS("RGMII: %#04x\n", u8); + + // close access to APC registers + gPCIModule->write_pci_config(pciInfo.bus, + pciInfo.device, pciInfo.function, 0x48, 1, reg); + + return B_OK; + } + } + } + + TRACE_ALWAYS("ISA bridge was not found.\n"); + return B_ERROR; +} + + +void +Device::DumpRegisters() +{ + struct RegisterEntry { + uint32 Base; + const char* Name; + bool writeBack; + } RegisterEntries[] = { + { TxControl, "TxControl", false }, + { TxBase, "TxBase\t", false }, + { TxStatus, "TxStatus", false }, + { TxReserved, "TxReserved", false }, + { RxControl, "RxControl", false }, + { RxBase, "RxBase\t", false }, + { RxStatus, "RxStatus", false }, + { RxReserved, "RxReserved", false }, + { IntSource, "IntSource", true }, + { IntMask, "IntMask", false }, + { IntControl, "IntControl", false }, + { IntTimer, "IntTimer", false }, + { PowControl, "PowControl", false }, + { Reserved0, "Reserved0", false }, + { EEPROMControl, "EEPROMCntl", false }, + { EEPROMInterface, "EEPROMIface", false }, + { StationControl, "StationCntl", false }, + { SMInterface, "SMInterface", false }, + { GIoCR, "GIoCR\t", false }, + { GIoControl, "GIoControl", false }, + { TxMACControl, "TxMACCntl", false }, + { TxLimit, "TxLimit", false }, + { RGDelay, "RGDelay", false }, + { Reserved1, "Reserved1", false }, + { RxMACControl, "RxMACCntlEtc", false }, + { RxMACAddress + 2, "RxMACAddr2", false }, + { RxHashTable, "RxHashTable1", false }, + { RxHashTable + 4, "RxHashTable2", false }, + { RxWOLControl, "RxWOLControl", false }, + { RxWOLData, "RxWOLData", false }, + { RxMPSControl, "RxMPSControl", false }, + { Reserved2, "Reserved2", false } + }; + + for (size_t i = 0; i < _countof(RegisterEntries); i++) { + uint32 registerContents = ReadPCI32(RegisterEntries[i].Base); + kprintf("%s:\t%08lx\n", RegisterEntries[i].Name, registerContents); + if (RegisterEntries[i].writeBack) { + WritePCI32(RegisterEntries[i].Base, registerContents); + } + } +} + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Device.h b/src/add-ons/kernel/drivers/network/sis19x/Device.h new file mode 100644 index 0000000000..0a45233fd0 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Device.h @@ -0,0 +1,127 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _SiS19X_DEVICE_H_ +#define _SiS19X_DEVICE_H_ + + +#include "Driver.h" +#include "MIIBus.h" +#include "Registers.h" +#include "DataRing.h" + +#include "Settings.h" //!!! + + +const uint32 MaxFrameSize = 1514; // 1536?? +const bigtime_t TransmitTimeout = 5000000; + + +const uint32 TxDescriptorsCount = 65;//34;//32; +const uint32 RxDescriptorsCount = 65;//64; +const uint32 TxDescriptorsMask = TxDescriptorsCount - 1; +const uint32 RxDescriptorsMask = RxDescriptorsCount - 1; + + +typedef DataRing TxDataRing; +typedef DataRing RxDataRing; + + +class Device : private timer { +public: + class Info { + public: + const uint32 fId; + const char* fName; + const char* fDescription; + inline const char* Name() { return fName; } + inline const char* Description() { return fName; } + inline uint16 DeviceId() { return DEVICEID(fId); } + inline uint16 VendorId() { return VENDORID(fId); } + inline uint32 Id() { return fId; } + }; + + Device(Info &DeviceInfo, pci_info &PCIInfo); + virtual ~Device(); + + status_t InitCheck() { return fStatus; }; + + status_t Open(uint32 flags); +// bool IsOpen() { return fOpen; }; + + status_t Close(); + status_t Free(); + + status_t Read(uint8 *buffer, size_t *numBytes); + status_t Write(const uint8 *buffer, size_t *numBytes); + status_t Control(uint32 op, void *buffer, size_t length); + + status_t SetupDevice(); + void TeardownDevice(); + + status_t _Reset(); + + uint8 ReadPCI8(int offset); + uint16 ReadPCI16(int offset); + uint32 ReadPCI32(int offset); + void WritePCI8(int offset, uint8 value); + void WritePCI16(int offset, uint16 value); + void WritePCI32(int offset, uint32 value); + + cpu_status Lock(); + void Unlock(cpu_status st); + +static int32 InterruptHandler(void *InterruptParam); +const ether_link_state& LinkState() const { return fMII.LinkState(); } + +protected: + + status_t GetLinkState(ether_link_state *state); + status_t ReadMACAddress(ether_address_t& address); + uint16 _ReadEEPROM(uint32 address); + void _InitRxFilter(); + status_t _SetRxMode(bool isPromiscuousModeOn); + +static int32 _TimerHandler(struct timer* timer); + + // state tracking + status_t fStatus; + pci_info fPCIInfo; + Info& fInfo; + int fIOBase; + int32 fHWSpinlock; + int32 fInterruptsNest; + + // interface and device infos + uint16 fFrameSize; + + // MII bus handler + MIIBus fMII; + + // connection data + ether_address_t fMACAddress; + +public: + + bool fOpen; + uint32 fBlockFlag; + + // connection data + sem_id fLinkStateChangeSem; + bool fHasConnection; + + TxDataRing fTxDataRing; + RxDataRing fRxDataRing; + + void DumpRegisters(); + +#if STATISTICS + Statistics fStatistics; +#endif +}; + +#endif //_SiS19X_DEVICE_H_ + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Driver.cpp b/src/add-ons/kernel/drivers/network/sis19x/Driver.cpp new file mode 100644 index 0000000000..bfce9dfb48 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Driver.cpp @@ -0,0 +1,283 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + + +#include "Driver.h" + +#include +#include +#include +#include + +#include "Device.h" +#include "Settings.h" + + +// TODO: Optimize buffers - use size 1536 instead of 2048 and dynamically determine count of descriptors. +// TODO: implement tx ring cleanup on reconnect (?) +// TODO: Tx speed is extremely low!!! Only 200 K/sek :-( + + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +size_t numCards = 0; +Device* gDevices[MAX_DEVICES] = {0}; +char* gDeviceNames[MAX_DEVICES + 1] = {0}; + +pci_module_info* gPCIModule = NULL; + + +static Device::Info cardInfos[] = { + { SiS190, "SiS190", "SiS 190 PCI Fast Ethernet Adapter" }, + { SiS191, "SiS191", "SiS 191 PCI Gigabit Ethernet Adapter" } +}; + + +status_t +init_hardware() +{ + TRACE_ALWAYS("SiS19X:init_hardware()\n"); + status_t result = get_module(B_PCI_MODULE_NAME, (module_info**)&gPCIModule); + if (result < B_OK) { + return ENOSYS; + } + + pci_info info = {0}; + for (long i = 0; B_OK == (*gPCIModule->get_nth_pci_info)(i, &info); i++) { + for (size_t idx = 0; idx < _countof(cardInfos); idx++) { + if (CARDID(info.vendor_id, info.device_id) == cardInfos[idx].Id()) { + TRACE_ALWAYS("Found:%s %#010x\n", + cardInfos[idx].Description(), cardInfos[idx].Id()); + put_module(B_PCI_MODULE_NAME); + return B_OK; + } + } + } + + put_module(B_PCI_MODULE_NAME); + return ENODEV; +} + + +static int SiS19X_DebuggerCommand(int argc, char** argv) +{ + const char* usageInfo = "usage:" DRIVER_NAME " [index] \n" + " - t - dump Transmit ring;\n" + " - r - dump Receive ring.\n" + " - g - dump reGisters.\n"; + + uint64 cardId = 0; + int cmdIndex = 1; + + if (argc < 2) { + kprintf(usageInfo); + return 0; + } else + if (argc > 2) { + cardId = parse_expression(argv[2]); + cmdIndex++; + } + + if (cardId >= numCards) { + kprintf("%lld - invalid index.\n", cardId); + kprintf(usageInfo); + return 0; + } + + Device* device = gDevices[cardId]; + if (device == NULL) { + kprintf("Invalid device pointer!!!.\n"); + return 0; + } + + switch(*argv[cmdIndex]) { + case 'g': device->DumpRegisters(); break; + case 't': device->fTxDataRing.Dump(); break; + case 'r': device->fRxDataRing.Dump(); break; + default: + kprintf("'%s' - invalid parameter\n", argv[cmdIndex]); + kprintf(usageInfo); + break; + } + + return 0; +} + + +status_t +init_driver() +{ + status_t status = get_module(B_PCI_MODULE_NAME, (module_info**)&gPCIModule); + if (status < B_OK) { + return ENOSYS; + } + + load_settings(); + + TRACE_ALWAYS("%s\n", kVersion); + + pci_info info = {0}; + for (long i = 0; B_OK == (*gPCIModule->get_nth_pci_info)(i, &info); i++) { + for (size_t idx = 0; idx < _countof(cardInfos); idx++) { + if (info.vendor_id == cardInfos[idx].VendorId() + && info.device_id == cardInfos[idx].DeviceId()) + { + TRACE_ALWAYS("Found:%s %#010x\n", + cardInfos[idx].Description(), cardInfos[idx].Id()); + + if (numCards == MAX_DEVICES) { + break; + } + + Device* device = new Device(cardInfos[idx], info); + if (device == 0) { + return ENODEV; + } + + status_t status = device->InitCheck(); + if (status < B_OK) { + delete device; + break; + } + + status = device->SetupDevice(); + if (status < B_OK) { + delete device; + break; + } + + char name[DEVNAME_LEN] = {0}; + sprintf(name, "net/%s/%ld", cardInfos[idx].Name(), numCards); + gDeviceNames[numCards] = strdup(name); + gDevices[numCards++] = device; + } + } + } + + if (numCards == 0) { + put_module(B_PCI_MODULE_NAME); + return ENODEV; + } + + add_debugger_command(DRIVER_NAME, SiS19X_DebuggerCommand, + "SiS190/191 Ethernet driver info"); + + return B_OK; +} + + +void +uninit_driver() +{ + remove_debugger_command(DRIVER_NAME, SiS19X_DebuggerCommand); + + for (size_t i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i]) { + gDevices[i]->TeardownDevice(); + delete gDevices[i]; + gDevices[i] = NULL; + } + + free(gDeviceNames[i]); + gDeviceNames[i] = NULL; + } + + put_module(B_PCI_MODULE_NAME); + + release_settings(); +} + + +static status_t +SiS19X_open(const char* name, uint32 flags, void** cookie) +{ + status_t status = ENODEV; + *cookie = NULL; + for (size_t i = 0; i < MAX_DEVICES; i++) { + if (gDeviceNames[i] && !strcmp(gDeviceNames[i], name)) { + status = gDevices[i]->Open(flags); + *cookie = gDevices[i]; + } + } + + return status; +} + + +static status_t +SiS19X_read(void* cookie, off_t position, void* buffer, size_t* numBytes) +{ + Device* device = (Device*)cookie; + return device->Read((uint8*)buffer, numBytes); +} + + +static status_t +SiS19X_write(void* cookie, off_t position, + const void* buffer, size_t* numBytes) +{ + Device* device = (Device*)cookie; + return device->Write((const uint8*)buffer, numBytes); +} + + +static status_t +SiS19X_control(void* cookie, uint32 op, void* buffer, size_t length) +{ + Device* device = (Device*) cookie; + return device->Control(op, buffer, length); +} + + +static status_t +SiS19X_close(void* cookie) +{ + Device* device = (Device*)cookie; + return device->Close(); +} + + +static status_t +SiS19X_free(void* cookie) +{ + Device* device = (Device*)cookie; + return device->Free(); +} + + +const char** +publish_devices() +{ + for (size_t i = 0; i < MAX_DEVICES; i++) { + if (gDevices[i] == NULL) + continue; + + if (gDeviceNames[i]) + TRACE("%s\n", gDeviceNames[i]); + } + + return (const char**)&gDeviceNames[0]; +} + + +device_hooks* +find_device(const char* name) +{ + static device_hooks deviceHooks = { + SiS19X_open, + SiS19X_close, + SiS19X_free, + SiS19X_control, + SiS19X_read, + SiS19X_write, + NULL, // select + NULL // deselect + }; + + return &deviceHooks; +} + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Driver.h b/src/add-ons/kernel/drivers/network/sis19x/Driver.h new file mode 100644 index 0000000000..90793796a6 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Driver.h @@ -0,0 +1,45 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _SiS19X_DRIVER_H_ +#define _SiS19X_DRIVER_H_ + + +#include +#include + +#define DRIVER_NAME "sis19x" +#define MAX_DEVICES 3 +#define DEVNAME_LEN 32 + +#define CARDID(vendor_id, device_id)\ + (((uint32)(vendor_id) << 16) | (device_id)) + +#define VENDORID(card_id) (((card_id) >> 16) & 0xffff) +#define DEVICEID(card_id) ((card_id) & 0xffff) + + +const char* const kVersion = "ver.1.0.0"; + +// ids for supported hardware +const uint32 SiS190 = CARDID(0x1039, 0x0190); +const uint32 SiS191 = CARDID(0x1039, 0x0191); + +extern pci_module_info* gPCIModule; + + +extern "C" { + +status_t init_hardware(); +status_t init_driver(); +void uninit_driver(); +const char** publish_devices(); +device_hooks* find_device(const char* name); + +} + +#endif //_SiS19X_DRIVER_H_ + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Jamfile b/src/add-ons/kernel/drivers/network/sis19x/Jamfile new file mode 100644 index 0000000000..4f58405f88 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Jamfile @@ -0,0 +1,15 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network sis19x ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders kernel net ; + +UsePrivateHeaders [ FDirName kernel util ] ; + +KernelAddon sis19x : + Driver.cpp + Device.cpp + MIIBus.cpp + DataRing.cpp + Settings.cpp + ; diff --git a/src/add-ons/kernel/drivers/network/sis19x/MIIBus.cpp b/src/add-ons/kernel/drivers/network/sis19x/MIIBus.cpp new file mode 100644 index 0000000000..772307c4c5 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/MIIBus.cpp @@ -0,0 +1,446 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + + +#include "MIIBus.h" + +#include + +#include "Driver.h" +#include "Settings.h" +#include "Device.h" +#include "Registers.h" + + +#define MII_OUI(id) ((id >> 10) & 0xffff) +#define MII_MODEL(id) ((id >> 4) & 0x003f) +#define MII_REV(id) ((id) & 0x000f) + +#define ISVALID(__address) ((__address) < 32) + +// the marker for not initialized or currently selected PHY address +const uint8 NotInitPHY = 0xff; + +// composite ids of PHYs suported by this driver +const uint32 BroadcomBCM5461 = CARDID(0x0020, 0x60c0); +const uint32 BroadcomAC131 = CARDID(0x0143, 0xbc70); +const uint32 AgereET1101B = CARDID(0x0282, 0xf010); +const uint32 Atheros = CARDID(0x004d, 0xd010); +const uint32 AtherosAR8012 = CARDID(0x004d, 0xd020); +const uint32 RealtekRTL8201 = CARDID(0x0000, 0x8200); +const uint32 Marvell88E1111 = CARDID(0x0141, 0x0cc0); +const uint32 UnknownPHY = CARDID(0x0000, 0x0000); + +MIIBus::ChipInfo miiChipTable[] = { + { BroadcomBCM5461, MIIBus::PHYLAN, "Broadcom BCM5461" }, + { BroadcomAC131, MIIBus::PHYLAN, "Broadcom AC131" }, + { AgereET1101B, MIIBus::PHYLAN, "Agere ET1101B" }, + { Atheros, MIIBus::PHYLAN, "Atheros" }, + { AtherosAR8012, MIIBus::PHYLAN, "Atheros AR8012" }, + { RealtekRTL8201, MIIBus::PHYLAN, "Realtek RTL8201" }, + { Marvell88E1111, MIIBus::PHYLAN, "Marvell 88E1111" }, + // unknown one must be the terminating entry! + { UnknownPHY, MIIBus::PHYUnknown, "Unknown PHY" } +}; + + +MIIBus::MIIBus(Device* device) + : + fDevice(device), + fSelectedPHY(NotInitPHY), + fGigagbitCapable(false), + fHasRGMII(false) +{ + memset(&fLinkState, 0, sizeof(fLinkState)); +} + + +status_t +MIIBus::Init() +{ + // reset to default state + fPHYs.MakeEmpty(); + + // iterate through all possible MII addresses + for (uint8 addr = 0; ISVALID(addr); addr++) { + uint16 miiStatus = _Read(MII_BMSR, addr); + + if (miiStatus == 0xffff || miiStatus == 0) + continue; + + uint32 Id = CARDID(_Read(MII_PHYID0, addr), _Read(MII_PHYID1, addr)); + + TRACE("MII Info(addr:%d,id:%#010x): OUI:%04x; Model:%04x; rev:%02x.\n", + addr, Id, MII_OUI(Id), MII_MODEL(Id), MII_REV(Id)); + + for (size_t i = 0; i < _countof(miiChipTable); i++){ + ChipInfo& info = miiChipTable[i]; + + if (info.fId != UnknownPHY && info.fId != (Id & 0xfffffff0)) + continue; + + fPHYs.Put(addr, info); + + break; + } + } + + if (fPHYs.IsEmpty()) { + TRACE_ALWAYS("No PHYs found.\n"); + return B_ENTRY_NOT_FOUND; + } + + // select appropriate PHY + Select(); + + // Marvell 88E1111 requires extra initialization + if (fPHYs.Get(fSelectedPHY).fId == Marvell88E1111) { + _Write(0x1b, (fHasRGMII ? 0x808b : 0x808f), fSelectedPHY); + spin(200); + _Write(0x14, (fHasRGMII ? 0x0ce1 : 0x0c60), fSelectedPHY); + spin(200); + } + + // some chips require reset + Reset(); + + return B_OK; +} + + +status_t +MIIBus::Select(uint16* currentStatus /*= NULL*/) +{ + if (fPHYs.IsEmpty()) { + TRACE_ALWAYS("Error: No PHYs found or available.\n"); + return B_ENTRY_NOT_FOUND; + } + + uint8 lanPHY = NotInitPHY; + uint8 homePHY = NotInitPHY; + fSelectedPHY = NotInitPHY; + + for (ChipInfoMap::Iterator i = fPHYs.Begin(); i != fPHYs.End(); i++) { + uint8 address = i->Key(); + ChipInfo& info = i->Value(); + + uint16 status = _Status(address); + + if ((status & BMSR_Link) && !ISVALID(fSelectedPHY) + && (info.fType != PHYUnknown)) + { + fSelectedPHY = address; + } else { + uint16 control = _Read(MII_BMCR, address); + control |= BMCR_Isolate | BMCR_ANegEnabled; + _Write(MII_BMCR, control, address); + + if (info.fType == PHYLAN) + lanPHY = address; + if (info.fType == PHYHome) + homePHY = address; + } + } + + if (!ISVALID(fSelectedPHY)) { + if (ISVALID(homePHY)) + fSelectedPHY = homePHY; + else if (ISVALID(lanPHY)) + fSelectedPHY = lanPHY; + else + fSelectedPHY = fPHYs.Begin()->Key(); + } + + uint16 control = _Read(MII_BMCR, fSelectedPHY); + control &= ~BMCR_Isolate; + _Write(MII_BMCR, control, fSelectedPHY); + +// TRACE("Selected PHY:%s\n", fPHYs.Get(fSelectedPHY).fName); + + if (currentStatus != NULL) { + *currentStatus = _Status(fSelectedPHY); + } + + return B_OK; +} + + +status_t +MIIBus::Reset(uint16* currentStatus /*=NULL*/) +{ + if (fPHYs.IsEmpty()) { + TRACE_ALWAYS("Error: No PHYs found or available.\n"); + return B_ENTRY_NOT_FOUND; + } + + uint16 status = _Status(fSelectedPHY); + _Write(MII_BMCR, BMCR_Reset | BMCR_ANegEnabled | BMCR_ANegRestart, fSelectedPHY); + + if (currentStatus != NULL) + *currentStatus = status; + + return B_OK; +} + + +void +MIIBus::_ControlSMInterface(uint32 control) +{ + fDevice->WritePCI32(SMInterface, control); + spin(10); + + for (size_t i = 0; i < 1000; i++) { + if ((fDevice->ReadPCI32(SMInterface) & SMIReq) == 0) { + return; + } + spin(10); + } + + TRACE_ALWAYS("Timeout writing SMI control.\n"); +} + + +uint16 +MIIBus::_Read(uint16 miiRegister, uint32 phyAddress) +{ + uint32 control = SMIOpRead | SMIReq; + control |= phyAddress << SMIPHYShift; + control |= miiRegister << SMIRegShift; + + _ControlSMInterface(control); + + return (fDevice->ReadPCI32(SMInterface) & SMIData) >> SMIDataShift; +} + + +status_t +MIIBus::Read(uint16 miiRegister, uint16 *value) +{ + if (fSelectedPHY >= 32) { + TRACE_ALWAYS("Error: MII is not ready\n"); + return B_ENTRY_NOT_FOUND; + } + + *value = _Read(miiRegister, fSelectedPHY); + + return B_OK; +} + + +void +MIIBus::_Write(uint16 miiRegister, uint16 value, uint32 phyAddress) +{ + uint32 control = SMIOpWrite | SMIReq; + control |= phyAddress << SMIPHYShift; + control |= miiRegister << SMIRegShift; + control |= value << SMIDataShift; + + _ControlSMInterface(control); +} + + +status_t +MIIBus::Write(uint16 miiRegister, uint16 value) +{ + if (fSelectedPHY >= 32) { + TRACE_ALWAYS("Error: MII is not ready\n"); + return B_ENTRY_NOT_FOUND; + } + + _Write(miiRegister, value, fSelectedPHY); + + return B_OK; +} + + +status_t +MIIBus::Status(uint16 *status) +{ + return Read(MII_BMSR, status); +} + + +uint16 +MIIBus::_Status(uint8 phyAddress) +{ + _Read(MII_BMSR, phyAddress); + return _Read(MII_BMSR, phyAddress); +} + + +bool +MIIBus::IsLinkUp() +{ + return (_Status(fSelectedPHY) & BMSR_Link) != 0; +} + + +uint32 +MIIBus::TimerHandler(bool* linkChanged) +{ + // XXX ? + /*if (!fNegotiationComplete) { + _UpdateLinkState(); + if ((fLinkState.media & IFM_ACTIVE) != 0) { + _SetMedia(); + } + return 0; + }*/ + + if ((fLinkState.media & IFM_ACTIVE) == 0) { + Select(); + if ((_Status(fSelectedPHY) & BMSR_Link) != 0) { + UpdateLinkState(); + SetMedia(); + if (fHasRGMII) { + if (fPHYs.Get(fSelectedPHY).fId == BroadcomBCM5461) { + _Write(0x18, 0xf1c7, fSelectedPHY); + spin(200); + _Write(0x1c, 0x8c00, fSelectedPHY); + } + + fDevice->WritePCI32(RGDelay, 0x0441); + fDevice->WritePCI32(RGDelay, 0x0440); + } + // start Rx + uint32 control = fDevice->ReadPCI32(RxControl); + control |= 0x00000010; + fDevice->WritePCI32(RxControl, control); + + *linkChanged = true; + } + } else { + if ((_Status(fSelectedPHY) & BMSR_Link) == 0) { + // stop Rx + uint32 control = fDevice->ReadPCI32(RxControl); + control &= ~(0x00000010); + fDevice->WritePCI32(RxControl, control); + UpdateLinkState(); + + *linkChanged = true; + } + } + + //if (*linkChanged) { + // TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n", + // (fLinkState.media & IFM_ACTIVE) ? "active" : "inactive", + // fLinkState.speed / 1000, + // (fLinkState.media & IFM_FULL_DUPLEX) ? "full" : "half"); + //} + + return 0; +} + + +status_t +MIIBus::UpdateLinkState(ether_link_state* state /*=NULL*/) +{ + if (state == NULL) { + state = &fLinkState; + } + + state->quality = 1000; + state->speed = 0; + state->media = IFM_ETHER; + + uint16 status = _Status(fSelectedPHY); + + if ((status & BMSR_Link) == 0) { + return B_OK; + } + + state->speed = 10000; + state->media |= IFM_ACTIVE; + + uint16 regAnar = _Read(MII_ANAR, fSelectedPHY); + uint16 regAnlpar = _Read(MII_ANLPAR, fSelectedPHY); + uint16 regAner = _Read(MII_ANER, fSelectedPHY); + + if (fGigagbitCapable && (regAnlpar & ANAR_NP) && (regAner & 0x0001)) { + uint16 regGAnar = _Read(MII_GANAR, fSelectedPHY); + uint16 regGAnlpar = _Read(MII_GANLPAR, fSelectedPHY); + + status = regGAnar & (regGAnlpar >> 2); + if (status & 0x0200) { + state->speed = 1000000; + state->media |= IFM_FULL_DUPLEX; + + } else if (status & 0x0100){ + state->speed = 1000000; + state->media |= IFM_HALF_DUPLEX; + + } else { + state->media |= IFM_HALF_DUPLEX; + } + + } else { + status = regAnar & regAnlpar; + + if (status & (ANAR_TX_HD | ANAR_TX_FD)) + state->speed = 100000; + if (status & (ANAR_TX_FD | ANAR_10_FD)) + state->media |= IFM_FULL_DUPLEX; + else + state->media |= IFM_HALF_DUPLEX; + } + + switch(state->speed) { + case 10000: state->media |= IFM_10_T; break; + case 100000: state->media |= IFM_100_TX; break; + case 1000000: state->media |= IFM_1000_T; break; + } + +// fNegotiationComplete = true; + return B_OK; +} + + +status_t +MIIBus::SetMedia(ether_link_state* state /*=NULL*/) +{ + if (state == NULL) { + state = &fLinkState; + } + + uint32 control = fDevice->ReadPCI32(StationControl); + + control &= ~(0x0f000000 | SC_FullDuplex | SC_Speed); + + switch(state->speed) { + case 1000000: + control |= (SC_Speed1000 | (0x3 << 24) | (0x1 << 26)); + break; + case 100000: + control |= (SC_Speed100 | (0x1 << 26)); + break; + case 10000: + control |= (SC_Speed10 | (0x1 << 26)); + break; + default: + TRACE_ALWAYS("Unsupported linkspeed:%d\n", state->speed); + break; + } + + if ((state->media & IFM_FULL_DUPLEX) != 0) { + control |= SC_FullDuplex; + } + + if (fHasRGMII) { + if (fPHYs.Get(fSelectedPHY).fId == BroadcomBCM5461) { + _Write(0x18, 0xf1c7, fSelectedPHY); + spin(200); + _Write(0x1c, 0x8c00, fSelectedPHY); + } + + control |= (0x3 << 24); + } + + fDevice->WritePCI32(StationControl, control); + + return B_OK; +} + diff --git a/src/add-ons/kernel/drivers/network/sis19x/MIIBus.h b/src/add-ons/kernel/drivers/network/sis19x/MIIBus.h new file mode 100644 index 0000000000..6e9a1f2c74 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/MIIBus.h @@ -0,0 +1,147 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _SiS19X_MII_BUS_H_ +#define _SiS19X_MII_BUS_H_ + + +#include +#include + +#include "Driver.h" + + +enum MII_Register { + MII_BMCR = 0x00, + MII_BMSR = 0x01, + MII_PHYID0 = 0x02, + MII_PHYID1 = 0x03, + MII_ANAR = 0x04, + MII_ANLPAR = 0x05, + MII_ANER = 0x06, + MII_GANAR = 0x09, + MII_GANLPAR = 0x0a +}; + + +enum MII_BMCR { + BMCR_FullDuplex = 0x0100, + BMCR_ANegRestart = 0x0200, + BMCR_Isolate = 0x0400, + BMCR_PowerDown = 0x0800, + BMCR_ANegEnabled = 0x1000, + BMCR_SpeedSelection = 0x2000, + BMCR_Loopback = 0x4000, + BMCR_Reset = 0x8000 +}; + + +enum MII_BMSR { + BMSR_CAP_100BASE_T4 = 0x8000, // PHY is able to perform 100base-T4 + BMSR_CAP_100BASE_TXFD = 0x4000, // PHY is able to perform 100base-TX full duplex + BMSR_CAP_100BASE_TXHD = 0x2000, // PHY is able to perform 100base-TX half duplex + BMSR_CAP_10BASE_TXFD = 0x1000, // PHY is able to perform 10base-TX full duplex + BMSR_CAP_10BASE_TXHD = 0x0800, // PHY is able to perform 10base-TX half duplex + BMSR_MFPS = 0x0040, // Management frame preamble supression + BMSR_ANC = 0x0020, // Auto-negotiation complete + BMSR_RF = 0x0010, // Remote fault + BMSR_CAP_AN = 0x0008, // PHY is able to perform auto-negotiation + BMSR_Link = 0x0004, // link state + BMSR_Jabber = 0x0002, // Jabber condition detected + BMSR_CAP_Ext = 0x0001 // Extended register capable +}; + + +enum MII_ANAR { + ANAR_NP = 0x8000, // Next page available + ANAR_ACK = 0x4000, // Link partner data reception ability acknowledged + ANAR_RF = 0x2000, // Fault condition detected and advertised + ANAR_PAUSE = 0x0400, // Pause operation enabled for full-duplex links + ANAR_T4 = 0x0200, // 100BASE-T4 supported + ANAR_TX_FD = 0x0100, // 100BASE-TX full duplex supported + ANAR_TX_HD = 0x0080, // 100BASE-TX half duplex supported + ANAR_10_FD = 0x0040, // 10BASE-TX full duplex supported + ANAR_10_HD = 0x0020, // 10BASE-TX half duplex supported + ANAR_SELECTOR = 0x0001 // Protocol selection bits (hardcoded to ethernet) +}; + + +enum MII_ANLPAR { + ANLPAR_NP = 0x8000, // Link partner next page enabled + ANLPAR_ACK = 0x4000, // Link partner data reception ability acknowledged + ANLPAR_RF = 0x2000, // Remote fault indicated by link partner + ANLPAR_PAUSE = 0x0400, // Pause operation supported by link partner + ANLPAR_T4 = 0x0200, // 100BASE-T4 supported by link partner + ANLPAR_TX_FD = 0x0100, // 100BASE-TX full duplex supported by link partner + ANLPAR_TX_HD = 0x0080, // 100BASE-TX half duplex supported by link partner + ANLPAR_10_FD = 0x0040, // 10BASE-TX full duplex supported by link partner + ANLPAR_10_HD = 0x0020, // 10BASE-TX half duplex supported by link partner + ANLPAR_SELECTOR = 0x0001 // Link partner's binary encoded protocol selector +}; + + +class Device; + +class MIIBus { +public: + enum Type { + PHYUnknown = 0, + PHYHome = 1, + PHYLAN = 2, + PHYMix = 3 + }; + + struct ChipInfo { + uint32 fId; + Type fType; + const char* fName; + }; + + typedef VectorMap ChipInfoMap; + + MIIBus(Device* device); + + status_t Init(); + status_t InitCheck(); + + status_t Read(uint16 miiRegister, uint16 *value); + status_t Write(uint16 miiRegister, uint16 value); + + status_t Status(uint16 *status); + status_t Select(uint16* status = NULL); + status_t Reset(uint16* status = NULL); + uint32 TimerHandler(bool* linkChanged); + + bool IsLinkUp(); + status_t UpdateLinkState(ether_link_state* state = NULL); + status_t SetMedia(ether_link_state* state = NULL); + + bool IsGigagbitCapable() { return fGigagbitCapable; } + void SetGigagbitCapable(bool on) { fGigagbitCapable = on; } + + bool HasRGMII() { return fHasRGMII; } + void SetRGMII(bool on) { fHasRGMII = on; } + + const ether_link_state& LinkState() const { return fLinkState; } + +private: + uint16 _Status(uint8 phyAddress); + void _ControlSMInterface(uint32 control); + uint16 _Read(uint16 miiRegister, uint32 phyAddress); + void _Write(uint16 miiRegister, uint16 value, uint32 phyAddress); + + Device* fDevice; + uint8 fSelectedPHY; + ChipInfoMap fPHYs; + + bool fGigagbitCapable; + bool fHasRGMII; + + ether_link_state fLinkState; +}; + +#endif //_SiS19X_MII_BUS_H_ + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Registers.h b/src/add-ons/kernel/drivers/network/sis19x/Registers.h new file mode 100644 index 0000000000..78431332e9 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Registers.h @@ -0,0 +1,265 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _SiS19X_REGISTERS_H_ +#define _SiS19X_REGISTERS_H_ + + +// Symbolic offset to registers +enum SiS19XRegisters { + TxControl = 0x00, // Tx Host Control / Status + TxBase = 0x04, // Tx Home Descriptor Base + TxReserved = 0x08, // Reserved + TxStatus = 0x0c, // Tx Next Descriptor Control / Status + RxControl = 0x10, // Rx Host Control / Status + RxBase = 0x14, // Rx Home Descriptor Base + RxReserved = 0x18, // Reserved + RxStatus = 0x1c, // Rx Next Descriptor Control / Status + IntSource = 0x20, // Interrupt Source + IntMask = 0x24, // Interrupt Mask + IntControl = 0x28, // Interrupt Control + IntTimer = 0x2c, // Interrupt Timer + PowControl = 0x30, // Power Management Control / Status + Reserved0 = 0x34, // Reserved + EEPROMControl = 0x38, // EEPROM Control / Status + EEPROMInterface = 0x3c, // EEPROM Interface + StationControl = 0x40, // Station Control / Status + SMInterface = 0x44, // Station Management Interface + GIoCR = 0x48, // GMAC IO Compensation + GIoControl = 0x4c, // GMAC IO Control + TxMACControl = 0x50, // Tx MAC Control + TxLimit = 0x54, // Tx MAC Timer / TryLimit + RGDelay = 0x58, // RGMII Tx Internal Delay Control + Reserved1 = 0x5c, // Reserved + RxMACControl = 0x60, // Rx MAC Control + RxMACAddress = 0x62, // Rx MAC Unicast Address + RxHashTable = 0x68, // Rx Multicast Hash Table + RxWOLControl = 0x70, // Rx WOL Control + RxWOLData = 0x74, // Rx WOL Data Access + RxMPSControl = 0x78, // Rx MPS Control + Reserved2 = 0x7c // Reserved +}; + + +// interrupt bits for IMR/ISR registers +enum SiS19XInterruptBits { + INT_SOFT = 0x40000000U, + INT_TIMER = 0x20000000U, + INT_PAUSEF = 0x00080000U, + INT_MAGICP = 0x00040000U, + INT_WAKEF = 0x00020000U, + INT_LINK = 0x00010000U, + INT_RXIDLE = 0x00000080U, + INT_RXDONE = 0x00000040U, + INT_TXIDLE = 0x00000008U, + INT_TXDONE = 0x00000004U, + INT_RXHALT = 0x00000002U, + INT_TXHALT = 0x00000001U +}; + + +const uint32 knownInterruptsMask = INT_LINK + /*| INT_RXIDLE*/ | INT_RXDONE + /*| INT_TXIDLE*/ | INT_TXDONE + | INT_RXHALT | INT_TXHALT; + + +// bits for RxControl register +enum SiS19XRxControlBits { + RxControlPoll = 0x00000010U, + RxControlEnable = 0x00000001U +}; + + +// bits for TxControl register +enum SiS19XTxControlBits { + TxControlPoll = 0x00000010U, + TxControlEnable = 0x00000001U +}; + + +// EEPROM Addresses +enum SiS19XEEPROMAddress { + EEPROMSignature = 0x00, + EEPROMClock = 0x01, + EEPROMInfo = 0x02, + EEPROMAddress = 0x03 +}; + + +// EEPROM Interface Register +enum SiS19XEEPROMInterface { + EIData = 0xffff0000, + EIDataShift = 16, + EIOffset = 0x0000fc00, + EIOffsetShift = 10, + EIOp = 0x00000300, + EIOpShift = 8, + EIOpRead = (2 << EIOpShift), + EIOpWrite = (1 << EIOpShift), + EIReq = 0x00000080, + EI_DO = 0x00000008, + EI_DI = 0x00000004, + EIClock = 0x00000002, + EI_CS = 0x00000001, + + EIInvalid = 0xffff // used as invalid readout from EEPROM +}; + + +// interrupt bits for Station Control registers +enum SiS19XStationControlBits { + SC_Loopback = 0x80000000U, + SC_RGMII = 0x00008000U, + SC_FullDuplex = 0x00001000U, + SC_Speed = 0x00000c00U, + SC_SpeedShift = 10, + SC_Speed1000 = (3U << SC_SpeedShift), + SC_Speed100 = (2U << SC_SpeedShift), + SC_Speed10 = (1U << SC_SpeedShift) +}; + + +// Station Management Interface Register +enum SiS19XSMInterface { + SMIData = 0xffff0000, + SMIDataShift = 16, + SMIReg = 0x0000f800, + SMIRegShift = 11, + SMIPHY = 0x000007c0, + SMIPHYShift = 6, + SMIOp = 0x00000020, + SMIOpShift = 5, + SMIOpWrite = (1 << SMIOpShift), + SMIOpRead = (0 << SMIOpShift), + SMIReq = 0x00000010, + SMI_MDIO = 0x00000008, + SMI_MDDIR = 0x00000004, + SMI_MDC = 0x00000002, + SMI_MDEN = 0x00000001 +}; + + +// transmit descriptor command bits +enum TxDescriptorCommandStatus { + TDC_TXOWN = 0x80000000U, // own bit + TDC_TXINT = 0x40000000U, + TDC_THOL3 = 0x30000000U, + TDC_THOL2 = 0x20000000U, + TDC_THOL1 = 0x10000000U, + TDC_THOL0 = 0x00000000U, + TDC_LSEN = 0x08000000U, + TDC_IPCS = 0x04000000U, + TDC_TCPCS = 0x02000000U, + TDC_UDPCS = 0x01000000U, + TDC_BSTEN = 0x00800000U, + TDC_EXTEN = 0x00400000U, + TDC_DEFEN = 0x00200000U, + TDC_BKFEN = 0x00100000U, + TDC_CRSEN = 0x00080000U, + TDC_COLSEN = 0x00040000U, + TDC_CRCEN = 0x00020000U, + TDC_PADEN = 0x00010000U, + // following bits are set/filled by hardware? + TDS_OWC = 0x00080000U, + TDS_ABT = 0x00040000U, + TDS_FIFO = 0x00020000U, + TDS_CRS = 0x00010000U, + TDS_COLLS = 0x0000ffffU +}; + + +const uint32 txErrorStatusBits = TDS_OWC | TDS_ABT | TDS_FIFO | TDS_CRS; +const uint32 TxDescriptorEOD = 0x80000000U; +const uint32 TxDescriptorSize = 0x0000ffffU; + + +struct TxDescriptor { + uint32 fPacketSize; + uint32 fCommandStatus; + uint32 fBufferPointer; + uint32 fEOD; + + void Init(phys_addr_t bufferPointer, bool bEOD) volatile { + fPacketSize = 0; + fCommandStatus = 0; + fBufferPointer = (uint32)bufferPointer; + fEOD = bEOD ? TxDescriptorEOD : 0; + } +}; + + +// receive descriptor information bits +enum RxDescriptorInformation { + RDI_RXOWN = 0x80000000U, + RDI_RXINT = 0x40000000U, + RDI_IPON = 0x20000000U, + RDI_TCPON = 0x10000000U, + RDI_UDPON = 0x08000000U, + RDI_WAKUP = 0x00400000U, + RDI_MAGIC = 0x00200000U, + RDI_PAUSE = 0x00100000U, + RDI_CAST = 0x000c0000U, + RDI_CAST_SHIFT = 18, + RDI_BCAST = ( 3U << RDI_CAST_SHIFT ), + RDI_MCAST = ( 2U << RDI_CAST_SHIFT ), + RDI_UCAST = ( 1U << RDI_CAST_SHIFT ), + RDI_CRCOFF = 0x00020000U, + RDI_PREADD = 0x00010000U +}; + + +// receive descriptor status bits +enum RxDescriptorStatus { + RDS_TAGON = 0x80000000U, + RDS_DESCS = 0x3f000000U, + RDS_DESCS_SHIFT = 24, + RDS_ABORT = 0x00800000U, + RDS_SHORT = 0x00400000U, + RDS_LIMIT = 0x00200000U, + RDS_MIIER = 0x00100000U, + RDS_OVRUN = 0x00080000U, + RDS_NIBON = 0x00040000U, + RDS_COLON = 0x00020000U, + RDS_CRCOK = 0x00010000U, + RDS_SIZE = 0x0000ffffU +}; + + +const uint32 rxErrorStatusBits = RDS_ABORT | RDS_SHORT | RDS_LIMIT + | RDS_MIIER | RDS_OVRUN | RDS_NIBON | RDS_COLON; +const uint32 RxDescriptorEOD = 0x80000000U; +const uint32 BufferSize = 1536; + +struct RxDescriptor { + uint32 fStatusSize; + uint32 fPacketInfo; + uint32 fBufferPointer; + uint32 fEOD; + + void Init(phys_addr_t bufferPointer, bool isEOD) volatile { + fStatusSize = 0; + fPacketInfo = RDI_RXOWN | RDI_RXINT; + fBufferPointer =(uint32) bufferPointer; + fEOD = isEOD ? RxDescriptorEOD : 0; + fEOD |= (BufferSize & 0x0000fff8); + } +}; + + +// RxMACControl bits +enum RxMACControlBits { + RXM_Broadcast = 0x0800U, + RXM_Multicast = 0x0400U, + RXM_Physical = 0x0200U, + RXM_AllPhysical = 0x0100U, + + RXM_Mask = RXM_Broadcast | RXM_Multicast + | RXM_Physical | RXM_AllPhysical +}; + +#endif // _SiS19X_REGISTERS_H_ + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Settings.cpp b/src/add-ons/kernel/drivers/network/sis19x/Settings.cpp new file mode 100644 index 0000000000..78413a2920 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Settings.cpp @@ -0,0 +1,207 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ + + +#include "Settings.h" + +#include +#include +#include + +#include // for mutex + + +bool gTraceOn = false; +bool gTruncateLogFile = false; +bool gTraceFlow = false; +bool gAddTimeStamp = true; +static char *gLogFilePath = NULL; + +mutex gLogLock; + + +// +// Logging, tracing and settings +// + +static +void create_log() +{ + if (gLogFilePath == NULL) + return; + + int flags = O_WRONLY | O_CREAT | ((gTruncateLogFile) ? O_TRUNC : 0); + close(open(gLogFilePath, flags, 0666)); + + mutex_init(&gLogLock, DRIVER_NAME"-logging"); +} + + +void load_settings() +{ + void *handle = load_driver_settings(DRIVER_NAME); + if (handle == 0) + return; + + gTraceOn = get_driver_boolean_parameter( + handle, "trace", gTraceOn, true); + + gTruncateLogFile = get_driver_boolean_parameter( + handle, "truncate_logfile", gTruncateLogFile, true); + + gTraceFlow = get_driver_boolean_parameter( + handle, "trace_flow", gTraceFlow, true); + + gAddTimeStamp = get_driver_boolean_parameter( + handle, "add_timestamp", gAddTimeStamp, true); + + const char * logFilePath = get_driver_parameter( + handle, "logfile", NULL, "/var/log/"DRIVER_NAME".log"); + if (logFilePath != NULL) { + gLogFilePath = strdup(logFilePath); + } + + unload_driver_settings(handle); + + create_log(); +} + + +void release_settings() +{ + if (gLogFilePath != NULL) { + mutex_destroy(&gLogLock); + free(gLogFilePath); + } +} + + +void SiS19X_trace(bool force, const char* func, const char *fmt, ...) +{ + if (!(force || gTraceOn)) { + return; + } + + va_list arg_list; + static const char *prefix = DRIVER_NAME":"; + static char buffer[1024]; + char *buf_ptr = buffer; + if (gLogFilePath == NULL) { + strcpy(buffer, prefix); + buf_ptr += strlen(prefix); + } + + if (gAddTimeStamp) { + bigtime_t time = system_time(); + uint32 msec = time / 1000; + uint32 sec = msec / 1000; + sprintf(buf_ptr, "%02ld.%02ld.%03ld:", + sec / 60, sec % 60, msec % 1000); + buf_ptr += strlen(buf_ptr); + } + + if (func != NULL) { + sprintf(buf_ptr, "%s::", func); + buf_ptr += strlen(buf_ptr); + } + + va_start(arg_list, fmt); + vsprintf(buf_ptr, fmt, arg_list); + va_end(arg_list); + + if (gLogFilePath == NULL) { + dprintf(buffer); + return; + } + + mutex_lock(&gLogLock); + int fd = open(gLogFilePath, O_WRONLY | O_APPEND); + write(fd, buffer, strlen(buffer)); + close(fd); + mutex_unlock(&gLogLock); +} + + +// +// Rx/Tx traffic statistic harvesting +// + +Statistics::Statistics() +{ + memset(this, 0, sizeof(Statistics)); +} + + +void +Statistics::PutStatus(uint32 status) +{ + fInterrupts++; + if (status & (INT_TXDONE /*| INT_TXIDLE*/)) fTxInterrupts++; + if (status & (INT_RXDONE /*| INT_RXIDLE*/)) fRxInterrupts++; + + if (status & INT_TXHALT) fTxHalt++; + if (status & INT_RXHALT) fRxHalt++; + if (status & INT_TXDONE) fTxDone++; + if (status & INT_TXIDLE) fTxIdle++; + if (status & INT_RXDONE) fRxDone++; + if (status & INT_RXIDLE) fRxIdle++; + if (status & INT_LINK) fLink++; + if (status & INT_WAKEF) fWakeUp++; + if (status & INT_MAGICP) fMagic++; + if (status & INT_PAUSEF) fPause++; + if (status & INT_TIMER) fTimer++; + if (status & INT_SOFT) fSoft++; +} + + +void +Statistics::PutTxStatus(uint32 status, uint32 size) +{ + if (status & TDS_CRS) fCarrier++; + if (status & TDS_FIFO) fFIFO++; + if (status & TDS_ABT) fTxAbort++; + if (status & TDS_OWC) fWindow++; + if ((status & txErrorStatusBits) == 0) { + fCollisions += (status & TDS_COLLS) - 1; + fTransmitted += (size & TxDescriptorSize); + } +} + + +void +Statistics::PutRxStatus(uint32 status) +{ + if (!(status & RDS_CRCOK)) fCRC++; + if (status & RDS_COLON) fColon++; + if (status & RDS_NIBON) fNibon++; + if (status & RDS_OVRUN) fOverrun++; + if (status & RDS_MIIER) fMIIError++; + if (status & RDS_LIMIT) fLimit++; + if (status & RDS_SHORT) fShort++; + if (status & RDS_ABORT) fRxAbort++; + if ((status & rxErrorStatusBits) == 0) { + fReceived += (status & RDS_SIZE) - 4; // exclude CRC? + } +} + + +void Statistics::Trace() +{ + TRACE("Ints:%d;Lnk:%d;WkUps:%d;Mgic:%d;Pause:%d;Tmr:%d;Sft:%d\n", + fInterrupts, fLink, fWakeUp, fMagic, fPause, fTimer, fSoft); + + TRACE("TX:Ints:%d;Bts:%llu;Drop:%d;Hlts:%d;Done:%d;Idle:%d;" + "Colls:%d;Carr:%d;FIFO:%d;Abrt:%d;Wndw:%d;\n", + fTxInterrupts, fTransmitted, fDropped, fTxHalt, fTxDone, + fTxIdle, fCollisions, fCarrier, fFIFO, fTxAbort, fWindow); + + TRACE("RX:Ints:%d;Bts:%llu;Hlts:%d;Done:%d;Idle:%d;CRC:%d;Cln:%d;" + "Nibon:%d;Ovrrn:%d;MIIErr:%d;Lmt:%d;Shrt:%d;Abrt:%d\n", + fRxInterrupts, fReceived, fRxHalt, fRxDone, fRxIdle, fCRC, fColon, + fNibon, fOverrun, fMIIError, fLimit, fShort, fRxAbort); +} + diff --git a/src/add-ons/kernel/drivers/network/sis19x/Settings.h b/src/add-ons/kernel/drivers/network/sis19x/Settings.h new file mode 100644 index 0000000000..6afc96bf6c --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis19x/Settings.h @@ -0,0 +1,92 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski + * Distributed under the terms of the MIT license. + * + */ +#ifndef _SiS19X_SETTINGS_H_ +#define _SiS19X_SETTINGS_H_ + + +#include + +#include "Driver.h" +#include "Registers.h" + + +#ifdef _countof +#warning "_countof(...) WAS ALREADY DEFINED!!! Remove local definition!" +#undef _countof +#endif +#define _countof(array)(sizeof(array) / sizeof(array[0])) + + +void load_settings(); +void release_settings(); + + +void SiS19X_trace(bool force, const char *func, const char *fmt, ...); + +#undef TRACE + +#define TRACE(x...) SiS19X_trace(false, __func__, x) +#define TRACE_ALWAYS(x...) SiS19X_trace(true, __func__, x) + +extern bool gTraceFlow; +#define TRACE_FLOW(x...) SiS19X_trace(gTraceFlow, NULL, x) + +#define TRACE_RET(result) SiS19X_trace(false, __func__, \ + "Returns:%#010x\n", result); + + +#define STATISTICS 1 + +struct Statistics { + Statistics(); + + void PutStatus(uint32 status); + void PutTxStatus(uint32 status, uint32 size); + void PutRxStatus(uint32 status); + void Trace(); + + + // shared + uint32 fInterrupts; + uint32 fLink; + uint32 fWakeUp; + uint32 fMagic; + uint32 fPause; + uint32 fTimer; + uint32 fSoft; + // transmit + uint32 fTxInterrupts; + uint32 fTxHalt; + uint32 fTxDone; + uint32 fTxIdle; + uint32 fCollisions; + uint32 fCarrier; + uint32 fFIFO; + uint32 fTxAbort; + uint32 fWindow; + uint32 fDropped; + uint64 fTransmitted; + + // receive + uint32 fRxInterrupts; + uint32 fRxHalt; + uint32 fRxDone; + uint32 fRxIdle; + uint32 fCRC; + uint32 fColon; + uint32 fNibon; + uint32 fOverrun; + uint32 fMIIError; + uint32 fLimit; + uint32 fShort; + uint32 fRxAbort; + uint64 fReceived; +}; + + +#endif /*_SiS19X_SETTINGS_H_*/ +