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:
@@ -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_*/
|
||||
|
||||
Reference in New Issue
Block a user