SiS190/191 NIC driver moved from the development branch to the trunk

to be available for using during build. It was requested by Frederik Modeen.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42822 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Siarzhuk Zharski
2011-10-11 14:59:28 +00:00
parent 2191dfe4bd
commit 522a82beb6
13 changed files with 2837 additions and 0 deletions
@@ -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 ;
@@ -0,0 +1,359 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <[email protected]>
* 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(phys_addr_t 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(phys_addr_t 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' );
}
}
}
@@ -0,0 +1,174 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <[email protected]>
* 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(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<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_
@@ -0,0 +1,676 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <[email protected]>
* 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) {
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);
}
}
}
@@ -0,0 +1,127 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <[email protected]>
* 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<TxDescriptor, TxDescriptorsCount> TxDataRing;
typedef DataRing<RxDescriptor, RxDescriptorsCount> 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_
@@ -0,0 +1,283 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include "Driver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lock.h>
#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] <t|r>\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;
}
@@ -0,0 +1,45 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS19X_DRIVER_H_
#define _SiS19X_DRIVER_H_
#include <Drivers.h>
#include <PCI.h>
#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_
@@ -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
;
@@ -0,0 +1,446 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
*/
#include "MIIBus.h"
#include <net/if_media.h>
#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;
}
@@ -0,0 +1,147 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS19X_MII_BUS_H_
#define _SiS19X_MII_BUS_H_
#include <ether_driver.h>
#include <util/VectorMap.h>
#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<uint8, ChipInfo> 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_
@@ -0,0 +1,265 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <imker@gmx.li>
* 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_
@@ -0,0 +1,207 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
*/
#include "Settings.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <lock.h> // 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);
}
@@ -0,0 +1,92 @@
/*
* SiS 190/191 NIC Driver.
* Copyright (c) 2009 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS19X_SETTINGS_H_
#define _SiS19X_SETTINGS_H_
#include <driver_settings.h>
#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_*/