Author: siarzhuk Date: 2009-12-03 21:59:10 +0100 (Thu, 03 Dec 2009) New Revision: 34476 Changeset: http://dev.haiku-os.org/changeset/34476/haiku Added: haiku/branches/developer/siarzhuk/ haiku/branches/developer/siarzhuk/sis19x/ haiku/branches/developer/siarzhuk/sis19x/DataRing.cpp haiku/branches/developer/siarzhuk/sis19x/DataRing.h haiku/branches/developer/siarzhuk/sis19x/Device.cpp haiku/branches/developer/siarzhuk/sis19x/Device.h haiku/branches/developer/siarzhuk/sis19x/Driver.cpp haiku/branches/developer/siarzhuk/sis19x/Driver.h haiku/branches/developer/siarzhuk/sis19x/Jamfile haiku/branches/developer/siarzhuk/sis19x/MIIBus.cpp haiku/branches/developer/siarzhuk/sis19x/MIIBus.h haiku/branches/developer/siarzhuk/sis19x/Registers.h haiku/branches/developer/siarzhuk/sis19x/Settings.cpp haiku/branches/developer/siarzhuk/sis19x/Settings.h haiku/branches/developer/siarzhuk/sis7018/ haiku/branches/developer/siarzhuk/usb_audio/ Log: - personal branch created; - usb_audio and sis7018 driver projects are branched for development and finishing; - sis190/191 NIC driver added to personal branch for finishing. Added: haiku/branches/developer/siarzhuk/sis19x/DataRing.cpp =================================================================== --- haiku/branches/developer/siarzhuk/sis19x/DataRing.cpp (rev 0) +++ haiku/branches/developer/siarzhuk/sis19x/DataRing.cpp 2009-12-03 20:59:10 UTC (rev 34476) @@ -0,0 +1,359 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski <imker@xxxxxx> + * Distributed under the terms of the MIT license. + * + */ + +#include "DataRing.h" + +#include <net/if_media.h> + +#include "Driver.h" +#include "Settings.h" +#include "Device.h" + + +// +// Tx stuff implementation +// + +template<> +void +DataRing<TxDescriptor, TxDescriptorsCount>::_SetBaseAddress(void* address) +{ + fDevice->WritePCI32(TxBase, (uint32)address); +} + + +template<> +status_t +DataRing<TxDescriptor, TxDescriptorsCount>::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<TxDescriptor, TxDescriptorsCount>::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<TxDescriptor, TxDescriptorsCount>::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<TxDescriptor, TxDescriptorsCount>::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<RxDescriptor, RxDescriptorsCount>::_SetBaseAddress(void* address) +{ + fDevice->WritePCI32(RxBase, (uint32)address); +} + + +template<> +int32 +DataRing<RxDescriptor, RxDescriptorsCount>::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<RxDescriptor, RxDescriptorsCount>::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<RxDescriptor, RxDescriptorsCount>::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' ); + } + } +} + Added: haiku/branches/developer/siarzhuk/sis19x/DataRing.h =================================================================== --- haiku/branches/developer/siarzhuk/sis19x/DataRing.h (rev 0) +++ haiku/branches/developer/siarzhuk/sis19x/DataRing.h 2009-12-03 20:59:10 UTC (rev 34476) @@ -0,0 +1,174 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski <imker@xxxxxx> + * Distributed under the terms of the MIT license. + * + */ +#ifndef _SiS19X_DATARING_H_ +#define _SiS19X_DATARING_H_ + + +#include <KernelExport.h> + +#include "Driver.h" +#include "Registers.h" +#include "Settings.h" + + +class Device; + +template<typename __type, uint32 __count> +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(void* 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<typename __type, uint32 __count> +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<typename __type, uint32 __count> +DataRing<__type, __count>::~DataRing() +{ + delete_sem(fSemaphore); + delete_area(fArea); +} + + +template<typename __type, uint32 __count> +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<typename __type, uint32 __count> +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<typename __type, uint32 __count> +status_t +DataRing<__type, __count>::Close() +{ + delete_sem(fSemaphore); + fSemaphore = 0; + + return B_OK; +} + + +template<typename __type, uint32 __count> +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_ + Added: haiku/branches/developer/siarzhuk/sis19x/Device.cpp =================================================================== --- haiku/branches/developer/siarzhuk/sis19x/Device.cpp (rev 0) +++ haiku/branches/developer/siarzhuk/sis19x/Device.cpp 2009-12-03 20:59:10 UTC (rev 34476) @@ -0,0 +1,624 @@ +/* + * SiS 190/191 NIC Driver. + * Copyright (c) 2009 S.Zharski <imker@xxxxxx> + * Distributed under the terms of the MIT license. + * + */ + + +#include "Device.h" + +#include <net/if_media.h> +#include <lock.h> + +#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) { [... truncated: 1856 lines follow ...]