Initial revision
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3226 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
be45fb85b6
commit
c0f1a43053
549
src/add-ons/kernel/drivers/network/sis900/device.c
Normal file
549
src/add-ons/kernel/drivers/network/sis900/device.c
Normal file
@ -0,0 +1,549 @@
|
||||
/* device.c - device hooks for SiS900 networking
|
||||
**
|
||||
** Copyright © 2001-2003 pinc Software. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <OS.h>
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
#include <PCI.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <image.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ether_driver.h"
|
||||
#include "driver.h"
|
||||
#include "device.h"
|
||||
#include "sis900.h"
|
||||
|
||||
|
||||
/* device hooks prototypes */
|
||||
|
||||
status_t device_open(const char *, uint32, void **);
|
||||
status_t device_close(void *);
|
||||
status_t device_free(void *);
|
||||
status_t device_ioctl(void *, uint32, void *, size_t);
|
||||
status_t device_read(void *, off_t, void *, size_t *);
|
||||
status_t device_write(void *, off_t, const void *, size_t *);
|
||||
|
||||
|
||||
int32 gDeviceOpenMask = 0;
|
||||
|
||||
device_hooks gDeviceHooks = {
|
||||
device_open,
|
||||
device_close,
|
||||
device_free,
|
||||
device_ioctl,
|
||||
device_read,
|
||||
device_write,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
//#define EXCESSIVE_DEBUG
|
||||
//#define THE_BUSY_WAITING_WAY
|
||||
|
||||
#ifdef EXCESSIVE_DEBUG
|
||||
//# include <time.h>
|
||||
|
||||
sem_id gIOLock;
|
||||
|
||||
int
|
||||
bug(const char *format, ...)
|
||||
{
|
||||
va_list vl;
|
||||
char c[2048];
|
||||
int i;
|
||||
int file;
|
||||
|
||||
if ((file = open("/boot/home/sis900-driver.log", O_RDWR | O_APPEND | O_CREAT)) >= 0) {
|
||||
// time_t timer = time(NULL);
|
||||
// strftime(c, 2048, "%H:%M:S: ", localtime(&timer));
|
||||
|
||||
va_start(vl, format);
|
||||
i = vsprintf(c, format, vl);
|
||||
va_end(vl);
|
||||
|
||||
write(file, c, strlen(c));
|
||||
close(file);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#define DUMPED_BLOCK_SIZE 16
|
||||
|
||||
void
|
||||
dumpBlock(const char *buffer, int size, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size;)
|
||||
{
|
||||
int start = i;
|
||||
|
||||
bug(prefix);
|
||||
for (; i < start+DUMPED_BLOCK_SIZE; i++)
|
||||
{
|
||||
if (!(i % 4))
|
||||
bug(" ");
|
||||
|
||||
if (i >= size)
|
||||
bug(" ");
|
||||
else
|
||||
bug("%02x", *(unsigned char *)(buffer + i));
|
||||
}
|
||||
bug(" ");
|
||||
|
||||
for (i = start; i < start + DUMPED_BLOCK_SIZE; i++)
|
||||
{
|
||||
if (i < size)
|
||||
{
|
||||
char c = buffer[i];
|
||||
|
||||
if (c < 30)
|
||||
bug(".");
|
||||
else
|
||||
bug("%c", c);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
bug("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* EXCESSIVE_DEBUG */
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
status_t
|
||||
checkDeviceInfo(struct sis_info *info)
|
||||
{
|
||||
if (!info || info->cookieMagic != SiS_COOKIE_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
deleteSemaphores(struct sis_info *info)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
createSemaphores(struct sis_info *info)
|
||||
{
|
||||
if ((info->rxSem = create_sem(0, "sis900 receive")) < B_OK)
|
||||
return info->rxSem;
|
||||
set_sem_owner(info->rxSem, B_SYSTEM_TEAM);
|
||||
|
||||
if ((info->txSem = create_sem(NUM_Tx_DESCR, "sis900 transmit")) < B_OK)
|
||||
{
|
||||
delete_sem(info->rxSem);
|
||||
return info->txSem;
|
||||
}
|
||||
set_sem_owner(info->txSem, B_SYSTEM_TEAM);
|
||||
|
||||
#ifdef EXCESSIVE_DEBUG
|
||||
if ((gIOLock = create_sem(1, "sis900 debug i/o lock")) < B_OK)
|
||||
return gIOLock;
|
||||
set_sem_owner(gIOLock, B_SYSTEM_TEAM);
|
||||
#endif
|
||||
|
||||
info->rxLock = info->txLock = 0;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// #pragma mark -
|
||||
// the device will be accessed through the following functions (a.k.a. device hooks)
|
||||
|
||||
|
||||
status_t
|
||||
device_open(const char *name, uint32 flags, void **cookie)
|
||||
{
|
||||
struct sis_info *info;
|
||||
area_id area;
|
||||
int id;
|
||||
|
||||
// verify device access
|
||||
{
|
||||
char *thisName;
|
||||
int32 mask;
|
||||
|
||||
// search for device name
|
||||
for (id = 0; (thisName = gDeviceNames[id]) != NULL; id++) {
|
||||
if (!strcmp(name, thisName))
|
||||
break;
|
||||
}
|
||||
if (!thisName)
|
||||
return EINVAL;
|
||||
|
||||
// check if device is already open
|
||||
mask = 1L << id;
|
||||
if (atomic_or(&gDeviceOpenMask, mask) & mask)
|
||||
return B_BUSY;
|
||||
}
|
||||
|
||||
// allocate internal device structure
|
||||
if ((area = create_area(DEVICE_NAME " private data", cookie,
|
||||
B_ANY_KERNEL_ADDRESS,
|
||||
ROUND_TO_PAGE_SIZE(sizeof(struct sis_info)),
|
||||
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK) {
|
||||
gDeviceOpenMask &= ~(1L << id);
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
info = (struct sis_info *)*cookie;
|
||||
memset(info, 0, sizeof(struct sis_info));
|
||||
|
||||
#ifdef DEBUG
|
||||
load_driver_symbols("sis900");
|
||||
#endif
|
||||
|
||||
info->cookieMagic = SiS_COOKIE_MAGIC;
|
||||
info->thisArea = area;
|
||||
info->id = id;
|
||||
info->pciInfo = pciInfo[id];
|
||||
info->registers = (char *)pciInfo[id]->u.h0.base_registers[0];
|
||||
|
||||
if (sis900_getMACAddress(info))
|
||||
dprintf(DEVICE_NAME ": MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
info->address.ebyte[0], info->address.ebyte[1], info->address.ebyte[2],
|
||||
info->address.ebyte[3], info->address.ebyte[4], info->address.ebyte[5]);
|
||||
else
|
||||
dprintf(DEVICE_NAME ": could not get MAC address\n");
|
||||
|
||||
if (createSemaphores(info) == B_OK) {
|
||||
status_t status = sis900_initPHYs(info);
|
||||
if (status == B_OK) {
|
||||
TRACE((DEVICE_NAME ": MII status = %d\n", mdio_read(info, MII_STATUS)));
|
||||
|
||||
//sis900_configFIFOs(info);
|
||||
sis900_reset(info);
|
||||
|
||||
// install & enable interrupts
|
||||
install_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line,
|
||||
sis900_interrupt, info, 0);
|
||||
sis900_enableInterrupts(info);
|
||||
|
||||
sis900_setRxFilter(info);
|
||||
sis900_createRings(info);
|
||||
|
||||
// enable receiver's state machine
|
||||
write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Rx_ENABLE);
|
||||
|
||||
// check link mode & add timer
|
||||
sis900_checkMode(info);
|
||||
add_timer(&info->timer, sis900_timer, 1000000LL, B_PERIODIC_TIMER);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
dprintf(DEVICE_NAME ": could not initialize MII PHY: %s\n", strerror(status));
|
||||
deleteSemaphores(info);
|
||||
} else
|
||||
dprintf(DEVICE_NAME ": could not create semaphores.\n");
|
||||
|
||||
gDeviceOpenMask &= ~(1L << id);
|
||||
delete_area(area);
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
device_close(void *data)
|
||||
{
|
||||
struct sis_info *info;
|
||||
|
||||
if (checkDeviceInfo(info = data) != B_OK)
|
||||
return EINVAL;
|
||||
|
||||
info->cookieMagic = SiS_FREE_COOKIE_MAGIC;
|
||||
|
||||
// cancel timer
|
||||
cancel_timer(&info->timer);
|
||||
|
||||
// disable the transmitter's and receiver's state machine
|
||||
write32((uint32)info->registers + SiS900_MAC_COMMAND,
|
||||
SiS900_MAC_CMD_Rx_DISABLE | SiS900_MAC_CMD_Tx_DISABLE);
|
||||
|
||||
// remove & disable interrupt
|
||||
sis900_disableInterrupts(info);
|
||||
remove_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line, sis900_interrupt, info);
|
||||
|
||||
delete_sem(info->rxSem);
|
||||
delete_sem(info->txSem);
|
||||
|
||||
#ifdef EXCESSIVE_DEBUG
|
||||
delete_sem(gIOLock);
|
||||
#endif
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
device_free(void *data)
|
||||
{
|
||||
struct sis_info *info = data;
|
||||
status_t retval = B_NO_ERROR;
|
||||
|
||||
if (info == NULL || info->cookieMagic != SiS_FREE_COOKIE_MAGIC)
|
||||
retval = EINVAL;
|
||||
|
||||
gDeviceOpenMask &= ~(1L << info->id);
|
||||
|
||||
sis900_deleteRings(info);
|
||||
delete_area(info->thisArea);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
device_ioctl(void *data, uint32 msg, void *buffer, size_t bufferLength)
|
||||
{
|
||||
struct sis_info *info;
|
||||
|
||||
if (checkDeviceInfo(info = data) != B_OK)
|
||||
return EINVAL;
|
||||
|
||||
switch (msg) {
|
||||
case ETHER_GETADDR:
|
||||
TRACE(("ioctl: get MAC address\n"));
|
||||
memcpy(buffer, &info->address, 6);
|
||||
return B_NO_ERROR;
|
||||
|
||||
case ETHER_INIT:
|
||||
TRACE(("ioctl: init\n"));
|
||||
return B_NO_ERROR;
|
||||
|
||||
case ETHER_GETFRAMESIZE:
|
||||
TRACE(("ioctl: get frame size\n"));
|
||||
*(uint32*)buffer = MAX_FRAME_SIZE;
|
||||
return B_NO_ERROR;
|
||||
|
||||
case ETHER_SETPROMISC:
|
||||
TRACE(("ioctl: set promisc\n"));
|
||||
sis900_setPromiscuous(info, *(uint32 *)buffer != 0);
|
||||
return B_OK;
|
||||
|
||||
case ETHER_NONBLOCK:
|
||||
TRACE(("ioctl: non blocking ? %s\n", info->blockFlag ? "yes" : "no"));
|
||||
info->blockFlag = *(int32 *)buffer ? B_TIMEOUT : 0;
|
||||
return B_NO_ERROR;
|
||||
|
||||
case ETHER_ADDMULTI:
|
||||
TRACE(("ioctl: add multicast\n"));
|
||||
/* not yet implemented */
|
||||
break;
|
||||
|
||||
default:
|
||||
TRACE(("ioctl: unknown message %d (length = %ld)\n", msg, bufferLength));
|
||||
break;
|
||||
}
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
// sis900.c
|
||||
extern int32 intrCounter;
|
||||
extern int32 lastIntr[100];
|
||||
uint32 counter = 0;
|
||||
#endif
|
||||
|
||||
|
||||
status_t
|
||||
device_read(void *data, off_t pos, void *buffer, size_t *_length)
|
||||
{
|
||||
struct sis_info *info;
|
||||
status_t status;
|
||||
size_t size;
|
||||
int32 blockFlag;
|
||||
thread_id threadID = find_thread(NULL);
|
||||
int32 rxp, rxd;
|
||||
uint32 check;
|
||||
int16 current;
|
||||
|
||||
if (checkDeviceInfo(info = data) != B_OK) {
|
||||
*_length = 0;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
//dprintf("read: rx: isr = %d, free = %d, current = %d\n",
|
||||
// info->rxInterruptIndex, info->rxFree, info->rxCurrent);
|
||||
|
||||
blockFlag = info->blockFlag;
|
||||
|
||||
// read is not reentrant
|
||||
if (atomic_or(&info->rxLock, 1)) {
|
||||
*_length = 0;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
//TRACE(("current rx descr: %08x (last = %ld)\n", rxp = read32((uint32)info->registers + SiS900_MAC_Rx_DESCR),(info->rxLast+1) % NUM_Rx_DESCR));
|
||||
|
||||
// block until data is available (if blocking is allowed)
|
||||
if ((status = acquire_sem_etc(info->rxSem, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_NO_ERROR) {
|
||||
TRACE(("cannot acquire read sem: %x, %s\n", status, strerror(status)));
|
||||
atomic_and(&info->rxLock, 0);
|
||||
*_length = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* three cases, frame is good, bad, or we don't own the descriptor */
|
||||
current = info->rxCurrent;
|
||||
check = info->rxDescriptor[current].status;
|
||||
|
||||
if (!(check & SiS900_DESCR_OWN)) { // the buffer is still in use!
|
||||
TRACE(("ERROR: read: buffer %d still in use: %x\n", current, status));
|
||||
atomic_and(&info->rxLock, 0);
|
||||
*_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (check & (SiS900_DESCR_Rx_ABORT | SiS900_DESCR_Rx_OVERRUN |
|
||||
SiS900_DESCR_Rx_LONG_PACKET | SiS900_DESCR_Rx_RUNT_PACKET |
|
||||
SiS900_DESCR_Rx_INVALID_SYM | SiS900_DESCR_Rx_FRAME_ALIGN |
|
||||
SiS900_DESCR_Rx_CRC_ERROR)) {
|
||||
TRACE(("ERROR read: packet with errors: %ld\n", check));
|
||||
*_length = 0;
|
||||
} else {
|
||||
// copy buffer
|
||||
size = (check & SiS900_DESCR_SIZE_MASK) - CRC_SIZE;
|
||||
if (size > MAX_FRAME_SIZE || size > *_length) {
|
||||
TRACE(("ERROR read: bad frame size %d\n", size));
|
||||
size = *_length;
|
||||
}
|
||||
memcpy(buffer, (void *)info->rxBuffer[current], size);
|
||||
}
|
||||
info->rxCurrent = (current + 1) & NUM_Rx_MASK;
|
||||
|
||||
/* update indexes and buffer ownership */
|
||||
{
|
||||
cpu_status former;
|
||||
former = disable_interrupts();
|
||||
acquire_spinlock(&info->rxSpinlock);
|
||||
|
||||
// release buffer to ring
|
||||
info->rxDescriptor[current].status = MAX_FRAME_SIZE + CRC_SIZE;
|
||||
info->rxFree++;
|
||||
|
||||
release_spinlock(&info->rxSpinlock);
|
||||
restore_interrupts(former);
|
||||
}
|
||||
|
||||
atomic_and(&info->rxLock, 0);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
device_write(void *data, off_t pos, const void *buffer, size_t *_length)
|
||||
{
|
||||
struct sis_info *info;
|
||||
status_t status;
|
||||
uint16 frameSize;
|
||||
int16 current;
|
||||
thread_id threadID = find_thread(NULL);
|
||||
uint32 check;
|
||||
|
||||
if (checkDeviceInfo(info = data) != B_OK)
|
||||
return EINVAL;
|
||||
|
||||
//TRACE(("****\t%5ld: write... %lx, %ld (%d) thread: %ld, counter = %ld\n", counter++, buf, *len, info->txLock, threadID, intrCounter));
|
||||
atomic_add(&info->txLock, 1);
|
||||
|
||||
if (*_length > MAX_FRAME_SIZE)
|
||||
*_length = MAX_FRAME_SIZE;
|
||||
|
||||
frameSize = *_length;
|
||||
current = info->txCurrent;
|
||||
|
||||
//dprintf("\t%5ld: \twrite: tx: isr = %d, sent = %d, current = %d\n",counter++,
|
||||
// info->txInterruptIndex,info->txSent,info->txCurrent);
|
||||
|
||||
// block until a free tx descriptor is available
|
||||
if ((status = acquire_sem_etc(info->txSem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT)) < B_NO_ERROR) {
|
||||
write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE);
|
||||
TRACE(("write: acquiring sem failed: %x, %s\n", status, strerror(status)));
|
||||
atomic_add(&info->txLock, -1);
|
||||
*_length = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
check = info->txDescriptor[current].status;
|
||||
if (check & SiS900_DESCR_OWN) {
|
||||
// descriptor is still in use
|
||||
dprintf(DEVICE_NAME ": card owns buffer %d\n", current);
|
||||
atomic_add(&info->txLock, -1);
|
||||
*_length = 0;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
/* Copy data to tx buffer */
|
||||
memcpy((void *)info->txBuffer[current], buffer, frameSize);
|
||||
info->txCurrent = (current + 1) & NUM_Tx_MASK;
|
||||
|
||||
{
|
||||
cpu_status former;
|
||||
former = disable_interrupts();
|
||||
acquire_spinlock(&info->txSpinlock);
|
||||
|
||||
info->txDescriptor[current].status = SiS900_DESCR_OWN | frameSize;
|
||||
info->txSent++;
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
struct buffer_desc *b = (void *)read32((uint32)info->registers + SiS900_MAC_Tx_DESCR);
|
||||
int16 that;
|
||||
|
||||
dprintf("\twrite: status %d = %lx, sent = %d\n", current, info->txDescriptor[current].status,info->txSent);
|
||||
dprintf("write: %d: mem = %lx : hardware = %lx\n", current, physicalAddress(&info->txDescriptor[current],sizeof(struct buffer_desc)),read32((uint32)info->registers + SiS900_MAC_Tx_DESCR));
|
||||
|
||||
for (that = 0;that < NUM_Tx_DESCR && (void *)physicalAddress(&info->txDescriptor[that],sizeof(struct buffer_desc)) != b;that++);
|
||||
|
||||
if (that == NUM_Tx_DESCR)
|
||||
{
|
||||
//dprintf("not in ring!\n");
|
||||
that = 0;
|
||||
}
|
||||
dprintf("(hardware status %d = %lx)!\n", that, info->txDescriptor[that].status);
|
||||
}
|
||||
#endif
|
||||
release_spinlock(&info->txSpinlock);
|
||||
restore_interrupts(former);
|
||||
}
|
||||
|
||||
// enable transmit state machine
|
||||
write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE);
|
||||
|
||||
#ifdef EXCESSIVE_DEBUG
|
||||
acquire_sem(gIOLock);
|
||||
bug("\t\twrite last interrupts:\n");
|
||||
{
|
||||
int ii;
|
||||
for (ii = (intrCounter-2) % 100; ii < intrCounter; ii = (ii + 1) % 100)
|
||||
bug("\t\t\t%ld: %08lx\n", ii, lastIntr[ii % 100]);
|
||||
}
|
||||
bug("\t\twrite block (%ld bytes) thread = %ld:\n", frameSize, threadID);
|
||||
dumpBlock(buf,frameSize, "\t\t\t");
|
||||
release_sem(gIOLock);
|
||||
#endif
|
||||
|
||||
atomic_add(&info->txLock, -1);
|
||||
return B_OK;
|
||||
}
|
||||
|
11
src/add-ons/kernel/drivers/network/sis900/device.h
Normal file
11
src/add-ons/kernel/drivers/network/sis900/device.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef DEVICE_H
|
||||
#define DEVICE_H
|
||||
|
||||
#include <Drivers.h>
|
||||
|
||||
#define ETHER_TRANSMIT_TIMEOUT ((bigtime_t)5000000) /* five seconds */
|
||||
|
||||
/* The published PCI bus interface device hooks */
|
||||
extern device_hooks gDeviceHooks;
|
||||
|
||||
#endif /* DEVICE_H */
|
152
src/add-ons/kernel/drivers/network/sis900/driver.c
Normal file
152
src/add-ons/kernel/drivers/network/sis900/driver.c
Normal file
@ -0,0 +1,152 @@
|
||||
/* driver - kernel driver for SiS900 networking
|
||||
**
|
||||
** Copyright © 2001-2003 pinc Software. All Rights Reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include <OS.h>
|
||||
#include <KernelExport.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <PCI.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "ether_driver.h"
|
||||
#include "driver.h"
|
||||
#include "device.h"
|
||||
#include "sis900.h"
|
||||
|
||||
#define MAX_CARDS 4
|
||||
|
||||
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||
|
||||
char *gDeviceNames[MAX_CARDS + 1];
|
||||
pci_info *pciInfo[MAX_CARDS];
|
||||
pci_module_info *pci;
|
||||
|
||||
extern status_t init_hardware(void);
|
||||
extern status_t init_driver(void);
|
||||
extern void uninit_driver(void);
|
||||
extern const char **publish_devices(void);
|
||||
extern device_hooks *find_device(const char *name);
|
||||
|
||||
|
||||
const char **
|
||||
publish_devices(void)
|
||||
{
|
||||
TRACE((DEVICE_NAME ": publish_devices()\n"));
|
||||
return (const char **)gDeviceNames;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
init_hardware(void)
|
||||
{
|
||||
TRACE((DEVICE_NAME ": init_hardware()\n"));
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
init_driver(void)
|
||||
{
|
||||
status_t status;
|
||||
pci_info *info;
|
||||
int i, found;
|
||||
|
||||
TRACE((DEVICE_NAME ": init_driver()\n"));
|
||||
|
||||
if ((status = get_module(B_PCI_MODULE_NAME,(module_info **)&pci)) != B_OK)
|
||||
{
|
||||
TRACE((DEVICE_NAME ": pci module unavailable\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
// find devices
|
||||
info = malloc(sizeof(pci_info));
|
||||
for (i = found = 0; (status = pci->get_nth_pci_info(i, info)) == B_OK; i++) {
|
||||
if (info->vendor_id == VENDOR_ID_SiS && info->device_id == DEVICE_ID_SiS900) {
|
||||
// enable bus mastering for device
|
||||
pci->write_pci_config(info->bus, info->device, info->function,
|
||||
PCI_command, 2,
|
||||
PCI_command_master | pci->read_pci_config(
|
||||
info->bus, info->device, info->function,
|
||||
PCI_command, 2));
|
||||
|
||||
pciInfo[found++] = info;
|
||||
dprintf(DEVICE_NAME ": revision = %lx\n", info->revision);
|
||||
|
||||
info = malloc(sizeof(pci_info));
|
||||
}
|
||||
}
|
||||
free(info);
|
||||
|
||||
if (found == 0) {
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
// create device name list
|
||||
{
|
||||
char name[32];
|
||||
|
||||
for (i = 0; i < found; i++) {
|
||||
sprintf(name, DEVICE_DRIVERNAME "/%d", i);
|
||||
gDeviceNames[i] = strdup(name);
|
||||
}
|
||||
gDeviceNames[i] = NULL;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
uninit_driver(void)
|
||||
{
|
||||
void *item;
|
||||
int index;
|
||||
|
||||
TRACE((DEVICE_NAME ": uninit_driver()\n"));
|
||||
|
||||
// free device names & pci info
|
||||
for (index = 0; (item = gDeviceNames[index]) != NULL; index++) {
|
||||
free(item);
|
||||
free(pciInfo[index]);
|
||||
}
|
||||
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
}
|
||||
|
||||
|
||||
device_hooks *
|
||||
find_device(const char *name)
|
||||
{
|
||||
int index;
|
||||
|
||||
TRACE((DEVICE_NAME ": find_device()\n"));
|
||||
|
||||
for (index = 0; gDeviceNames[index] != NULL; index++) {
|
||||
if (!strcmp(name, gDeviceNames[index]))
|
||||
return &gDeviceHooks;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
void
|
||||
wake_driver(void)
|
||||
{
|
||||
// for compatibility with Dano, only
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
suspend_driver(void)
|
||||
{
|
||||
// for compatibility with Dano, only
|
||||
}
|
||||
*/
|
34
src/add-ons/kernel/drivers/network/sis900/driver.h
Normal file
34
src/add-ons/kernel/drivers/network/sis900/driver.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef DRIVER_H
|
||||
#define DRIVER_H
|
||||
/* driver - kernel driver for SiS900 networking
|
||||
**
|
||||
** Copyright © 2001-2003 pinc Software. All Rights Reserved.
|
||||
*/
|
||||
|
||||
|
||||
// PCI Communications
|
||||
|
||||
#define IO_PORT_PCI_ACCESS true
|
||||
//#define MEMORY_MAPPED_PCI_ACCESS true
|
||||
|
||||
#if IO_PORT_PCI_ACCESS
|
||||
# define write8(address,value) (*pci->write_io_8)((address),(value))
|
||||
# define write16(address,value) (*pci->write_io_16)((address),(value))
|
||||
# define write32(address,value) (*pci->write_io_32)((address),(value))
|
||||
# define read8(address) ((*pci->read_io_8)(address))
|
||||
# define read16(address) ((*pci->read_io_16)(address))
|
||||
# define read32(address) ((*pci->read_io_32)(address))
|
||||
#else /* MEMORY_MAPPED_PCI_ACCESS */
|
||||
# define read8(address) (*((volatile uint8*)(address)))
|
||||
# define read16(address) (*((volatile uint16*)(address)))
|
||||
# define read32(address) (*((volatile uint32*)(address)))
|
||||
# define write8(address,data) (*((volatile uint8 *)(address)) = data)
|
||||
# define write16(address,data) (*((volatile uint16 *)(address)) = (data))
|
||||
# define write32(address,data) (*((volatile uint32 *)(address)) = (data))
|
||||
#endif
|
||||
|
||||
extern char *gDeviceNames[];
|
||||
extern pci_info *pciInfo[];
|
||||
extern pci_module_info *pci;
|
||||
|
||||
#endif /* DRIVER_H */
|
57
src/add-ons/kernel/drivers/network/sis900/ether_driver.h
Normal file
57
src/add-ons/kernel/drivers/network/sis900/ether_driver.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* ether_driver.h
|
||||
*
|
||||
* Ethernet driver: handles NE2000 and 3C503 cards
|
||||
*/
|
||||
/*
|
||||
Copyright 1999, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
#ifndef _ETHER_DRIVER_H
|
||||
#define _ETHER_DRIVER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <Drivers.h>
|
||||
|
||||
/*
|
||||
* ioctls: belongs in a public header file
|
||||
* somewhere, so that the net_server and other ethernet drivers can use.
|
||||
*/
|
||||
enum {
|
||||
ETHER_GETADDR = B_DEVICE_OP_CODES_END, /* get ethernet address */
|
||||
ETHER_INIT, /* set irq and port */
|
||||
ETHER_NONBLOCK, /* set/unset nonblocking mode */
|
||||
ETHER_ADDMULTI, /* add multicast addr */
|
||||
ETHER_REMMULTI, /* rem multicast addr */
|
||||
ETHER_SETPROMISC, /* set promiscuous */
|
||||
ETHER_GETFRAMESIZE /* get frame size */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 48-bit ethernet address, passed back from ETHER_GETADDR
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char ebyte[6];
|
||||
} ether_address_t;
|
||||
|
||||
|
||||
/*
|
||||
* info passed to ETHER_INIT
|
||||
*/
|
||||
|
||||
typedef struct ether_init_params {
|
||||
short port;
|
||||
short irq;
|
||||
unsigned long mem;
|
||||
} ether_init_params_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _ETHER_DRIVER_H */
|
221
src/add-ons/kernel/drivers/network/sis900/interface.c
Normal file
221
src/add-ons/kernel/drivers/network/sis900/interface.c
Normal file
@ -0,0 +1,221 @@
|
||||
/* interface.c - MII, MDIO and EEPROM interface of SiS900
|
||||
**
|
||||
** Copyright 2001 pinc Software. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <OS.h>
|
||||
#include <KernelExport.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <PCI.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "ether_driver.h"
|
||||
#include "driver.h"
|
||||
#include "device.h"
|
||||
#include "interface.h"
|
||||
#include "sis900.h"
|
||||
|
||||
|
||||
// EEPROM access definitions
|
||||
#define EEPROM_DELAY() read32(eepromAccess)
|
||||
#define EEPROM_READ32() read32(eepromAccess)
|
||||
#define EEPROM_WRITE8(value) write8(eepromAccess,value)
|
||||
#define EEPROM_WRITE32(value) write32(eepromAccess,value)
|
||||
|
||||
|
||||
uint16 eeprom_read(struct sis_info *info,int address)
|
||||
{
|
||||
long eepromAccess = (long)info->registers + SiS900_MAC_EEPROM_ACCESS;
|
||||
uint32 readCmd = SiS900_EEPROM_CMD_READ | address;
|
||||
uint16 retval = 0;
|
||||
int i;
|
||||
|
||||
EEPROM_WRITE32(0);
|
||||
EEPROM_DELAY();
|
||||
EEPROM_WRITE32(SiS900_EEPROM_CLOCK);
|
||||
EEPROM_DELAY();
|
||||
|
||||
// Shift the read command (9) bits out.
|
||||
for (i = 8;i >= 0;i--)
|
||||
{
|
||||
uint32 value = (readCmd & (1 << i)) ? SiS900_EEPROM_DATA_IN | SiS900_EEPROM_SELECT
|
||||
: SiS900_EEPROM_SELECT;
|
||||
|
||||
EEPROM_WRITE32(value);
|
||||
EEPROM_DELAY();
|
||||
EEPROM_WRITE32(value | SiS900_EEPROM_CLOCK);
|
||||
EEPROM_DELAY();
|
||||
}
|
||||
EEPROM_WRITE8(SiS900_EEPROM_SELECT);
|
||||
EEPROM_DELAY();
|
||||
|
||||
// read in the 16 bit data
|
||||
for (i = 16;i > 0;i--)
|
||||
{
|
||||
EEPROM_WRITE32(SiS900_EEPROM_SELECT);
|
||||
EEPROM_DELAY();
|
||||
EEPROM_WRITE32(SiS900_EEPROM_SELECT | SiS900_EEPROM_CLOCK);
|
||||
EEPROM_DELAY();
|
||||
retval = (retval << 1) | ((EEPROM_READ32() & SiS900_EEPROM_DATA_OUT) ? 1 : 0);
|
||||
EEPROM_DELAY();
|
||||
}
|
||||
|
||||
EEPROM_WRITE32(0);
|
||||
EEPROM_DELAY();
|
||||
EEPROM_WRITE32(SiS900_EEPROM_CLOCK);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**************************** MII/MDIO ****************************/
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
#define mdio_delay(address) read32(address)
|
||||
|
||||
|
||||
static void
|
||||
mdio_idle(uint32 address)
|
||||
{
|
||||
write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR);
|
||||
mdio_delay(address);
|
||||
write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR | SiS900_MII_MDC);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mdio_reset(uint32 address)
|
||||
{
|
||||
int32 i;
|
||||
|
||||
for (i = 32; i-- > 0;)
|
||||
{
|
||||
write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR);
|
||||
mdio_delay(address);
|
||||
write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR | SiS900_MII_MDC);
|
||||
mdio_delay(address);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mdio_writeToPHY(struct sis_info *info,uint16 phy,uint16 reg,uint16 value)
|
||||
{
|
||||
uint32 address = (uint32)info->registers + SiS900_MAC_EEPROM_ACCESS;
|
||||
int32 cmd = MII_CMD_WRITE | (phy << MII_PHY_SHIFT) | (reg << MII_REG_SHIFT);
|
||||
int i;
|
||||
|
||||
mdio_reset(address);
|
||||
mdio_idle(address);
|
||||
|
||||
// issue the command
|
||||
for (i = 16;i-- > 0;)
|
||||
{
|
||||
int32 data = SiS900_MII_MDDIR | (cmd & (1 << i) ? SiS900_MII_MDIO : 0);
|
||||
|
||||
write8(address,data);
|
||||
mdio_delay(address);
|
||||
write8(address,data | SiS900_MII_MDC);
|
||||
mdio_delay(address);
|
||||
}
|
||||
mdio_delay(address);
|
||||
|
||||
// write the value
|
||||
for (i = 16;i-- > 0;)
|
||||
{
|
||||
int32 data = SiS900_MII_MDDIR | (value & (1 << i) ? SiS900_MII_MDIO : 0);
|
||||
|
||||
write32(address,data);
|
||||
mdio_delay(address);
|
||||
write32(address,data | SiS900_MII_MDC);
|
||||
mdio_delay(address);
|
||||
}
|
||||
mdio_delay(address);
|
||||
|
||||
// clear extra bits
|
||||
for (i = 2;i-- > 0;)
|
||||
{
|
||||
write8(address,0);
|
||||
mdio_delay(address);
|
||||
write8(address,SiS900_MII_MDC);
|
||||
mdio_delay(address);
|
||||
}
|
||||
write32(address,0);
|
||||
}
|
||||
|
||||
|
||||
uint16
|
||||
mdio_readFromPHY(struct sis_info *info,uint16 phy,uint16 reg)
|
||||
{
|
||||
uint32 address = (uint32)info->registers + SiS900_MAC_EEPROM_ACCESS;
|
||||
int32 cmd = MII_CMD_READ | (phy << MII_PHY_SHIFT) | (reg << MII_REG_SHIFT);
|
||||
uint16 value = 0;
|
||||
int i;
|
||||
|
||||
mdio_reset(address);
|
||||
mdio_idle(address);
|
||||
|
||||
for (i = 16; i-- > 0;)
|
||||
{
|
||||
int32 data = SiS900_MII_MDDIR | (cmd & (1 << i) ? SiS900_MII_MDIO : 0);
|
||||
|
||||
write32(address,data);
|
||||
mdio_delay(address);
|
||||
write32(address,data | SiS900_MII_MDC);
|
||||
mdio_delay(address);
|
||||
}
|
||||
|
||||
// read the value
|
||||
for (i = 16;i-- > 0;)
|
||||
{
|
||||
write32(address,0);
|
||||
mdio_delay(address);
|
||||
value = (value << 1) | (read32(address) & SiS900_MII_MDIO ? 1 : 0);
|
||||
write32(address,SiS900_MII_MDC);
|
||||
mdio_delay(address);
|
||||
}
|
||||
write32(address,0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
uint16
|
||||
mdio_read(struct sis_info *info, uint16 reg)
|
||||
{
|
||||
return mdio_readFromPHY(info,info->phy,reg);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mdio_write(struct sis_info *info, uint16 reg, uint16 value)
|
||||
{
|
||||
mdio_writeToPHY(info,info->phy,reg,value);
|
||||
}
|
||||
|
||||
|
||||
uint16
|
||||
mdio_statusFromPHY(struct sis_info *info, uint16 phy)
|
||||
{
|
||||
uint16 status;
|
||||
int i = 0;
|
||||
|
||||
// the status must be retrieved two times, because the first
|
||||
// one may not work on some PHYs (notably ICS 1893)
|
||||
while (i++ < 2)
|
||||
status = mdio_readFromPHY(info,phy,MII_STATUS);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
uint16
|
||||
mdio_status(struct sis_info *info)
|
||||
{
|
||||
return mdio_statusFromPHY(info,info->phy);
|
||||
}
|
||||
|
17
src/add-ons/kernel/drivers/network/sis900/interface.h
Normal file
17
src/add-ons/kernel/drivers/network/sis900/interface.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef INTERFACE_H
|
||||
#define INTERFACE_H
|
||||
|
||||
#include "sis900.h"
|
||||
|
||||
extern uint16 eeprom_read(struct sis_info *info,int address);
|
||||
|
||||
extern uint16 mdio_read(struct sis_info *info,uint16 reg);
|
||||
extern uint16 mdio_readFromPHY(struct sis_info *info,uint16 phy,uint16 reg);
|
||||
extern void mdio_write(struct sis_info *info,uint16 reg,uint16 value);
|
||||
extern void mdio_writeToPHY(struct sis_info *info,uint16 phy,uint16 reg,uint16 value);
|
||||
|
||||
extern uint16 mdio_statusFromPHY(struct sis_info *info, uint16 phy);
|
||||
extern uint16 mdio_status(struct sis_info *info);
|
||||
|
||||
|
||||
#endif /* INTERFACE_H */
|
774
src/add-ons/kernel/drivers/network/sis900/sis900.c
Normal file
774
src/add-ons/kernel/drivers/network/sis900/sis900.c
Normal file
@ -0,0 +1,774 @@
|
||||
/* sis900.c - SiS900 chip specific functions
|
||||
**
|
||||
** Copyright © 2001-2003 pinc Software. All Rights Reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include <OS.h>
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
#include <PCI.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ether_driver.h"
|
||||
#include "driver.h"
|
||||
#include "device.h"
|
||||
#include "interface.h"
|
||||
#include "sis900.h"
|
||||
|
||||
|
||||
// prototypes
|
||||
|
||||
uint16 sis900_resetPHY(struct sis_info *info);
|
||||
void sis900_selectPHY(struct sis_info *info);
|
||||
void sis900_setMode(struct sis_info *info, int32 mode);
|
||||
int32 sis900_readMode(struct sis_info *info);
|
||||
|
||||
|
||||
// MII chip info table
|
||||
|
||||
#define PHY_ID0_SiS900_INTERNAL 0x001d
|
||||
#define PHY_ID1_SiS900_INTERNAL 0x8000
|
||||
#define PHY_ID0_ICS_1893 0x0015
|
||||
#define PHY_ID1_ICS_1893 0xff40
|
||||
#define PHY_ID0_REALTEK_8201 0x0000
|
||||
#define PHY_ID1_REALTEK_8201 0x8200
|
||||
#define MII_HOME 0x0001
|
||||
#define MII_LAN 0x0002
|
||||
|
||||
const static struct mii_chip_info
|
||||
{
|
||||
const char *name;
|
||||
uint16 id0,id1;
|
||||
uint8 types;
|
||||
}
|
||||
gMIIChips[] = {
|
||||
{"SiS 900 Internal MII PHY", PHY_ID0_SiS900_INTERNAL,PHY_ID1_SiS900_INTERNAL,MII_LAN},
|
||||
{"SiS 7014 Physical Layer Solution",0x0016, 0xf830, MII_LAN},
|
||||
{"AMD 79C901 10BASE-T PHY", 0x0000, 0x6B70, MII_LAN},
|
||||
{"AMD 79C901 HomePNA PHY", 0x0000, 0x6B90, MII_HOME},
|
||||
{"ICS 1893 LAN PHY", PHY_ID0_ICS_1893,PHY_ID1_ICS_1893,MII_LAN},
|
||||
{"NS 83851 PHY", 0x2000, 0x5C20, MII_LAN | MII_HOME},
|
||||
{"Realtek RTL8201 PHY", PHY_ID0_REALTEK_8201,PHY_ID1_REALTEK_8201,MII_LAN},
|
||||
{NULL,0,0,0}
|
||||
};
|
||||
|
||||
|
||||
/***************************** helper functions *****************************/
|
||||
|
||||
uint32
|
||||
physicalAddress(volatile void *address,uint32 length)
|
||||
{
|
||||
physical_entry table;
|
||||
|
||||
get_memory_map((void *)address,length,&table,1);
|
||||
return (uint32)table.address;
|
||||
}
|
||||
|
||||
|
||||
/***************************** interrupt handling *****************************/
|
||||
// #pragma mark -
|
||||
int32 intrCounter = 0;
|
||||
int32 lastIntr[100];
|
||||
|
||||
|
||||
int32
|
||||
sis900_rxInterrupt(struct sis_info *info)
|
||||
{
|
||||
int32 handled = B_UNHANDLED_INTERRUPT;
|
||||
int16 releaseRxSem = 0;
|
||||
int16 limit;
|
||||
|
||||
acquire_spinlock(&info->rxSpinlock);
|
||||
|
||||
HACK(spin(10000));
|
||||
|
||||
// check for packet ownership
|
||||
for (limit = info->rxFree; limit > 0; limit--)
|
||||
{
|
||||
if (!(info->rxDescriptor[info->rxInterruptIndex].status & SiS900_DESCR_OWN))
|
||||
{
|
||||
// if (limit == info->rxFree)
|
||||
// {
|
||||
//dprintf("here!\n");
|
||||
// limit++;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
//dprintf("received frame %d!\n",info->rxInterruptIndex);
|
||||
releaseRxSem++;
|
||||
info->rxInterruptIndex = (info->rxInterruptIndex + 1) & NUM_Rx_MASK;
|
||||
info->rxFree--;
|
||||
}
|
||||
release_spinlock(&info->rxSpinlock);
|
||||
|
||||
// reenable rx queue
|
||||
write32((uint32)info->registers + SiS900_MAC_COMMAND,SiS900_MAC_CMD_Rx_ENABLE);
|
||||
|
||||
if (releaseRxSem)
|
||||
{
|
||||
release_sem_etc(info->rxSem,releaseRxSem,B_DO_NOT_RESCHEDULE);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
sis900_txInterrupt(struct sis_info *info)
|
||||
{
|
||||
int16 releaseTxSem = 0;
|
||||
uint32 status;
|
||||
int16 limit;
|
||||
|
||||
acquire_spinlock(&info->txSpinlock);
|
||||
|
||||
HACK(spin(10000));
|
||||
|
||||
//dprintf("here we are: sent = %d\n",info->txSent);
|
||||
for (limit = info->txSent; limit > 0; limit--)
|
||||
{
|
||||
status = info->txDescriptor[info->txInterruptIndex].status;
|
||||
|
||||
//dprintf("txIntr: %d: mem = %lx : hardware = %lx\n",info->txInterruptIndex,
|
||||
// physicalAddress(&info->txDescriptor[info->txInterruptIndex],sizeof(struct buffer_desc)),
|
||||
// read32((uint32)info->registers + SiS900_MAC_Tx_DESCR));
|
||||
|
||||
/* Does the device generate extra interrupts? */
|
||||
if (status & SiS900_DESCR_OWN)
|
||||
{
|
||||
struct buffer_desc *b = (void *)read32((uint32)info->registers + SiS900_MAC_Tx_DESCR);
|
||||
int16 that;
|
||||
for (that = 0;that < NUM_Tx_DESCR && (void *)physicalAddress(&info->txDescriptor[that],sizeof(struct buffer_desc)) != b;that++);
|
||||
if (that == NUM_Tx_DESCR)
|
||||
{
|
||||
//dprintf("not in ring!\n");
|
||||
that = 0;
|
||||
}
|
||||
//dprintf("tx busy %d: %lx (hardware status %d = %lx)!\n",info->txInterruptIndex,status,that,info->txDescriptor[that].status);
|
||||
// if (limit == info->txSent)
|
||||
// {
|
||||
//dprintf("oh no!\n");
|
||||
// limit++;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
|
||||
//dprintf("tx %d!\n",info->txInterruptIndex);
|
||||
if (status & (SiS900_DESCR_Tx_ABORT | SiS900_DESCR_Tx_UNDERRUN |
|
||||
SiS900_DESCR_Tx_OOW_COLLISION))
|
||||
dprintf("tx error: %x\n", status);
|
||||
else
|
||||
info->txDescriptor[info->txInterruptIndex].status = 0;
|
||||
|
||||
releaseTxSem++; /* this many buffers are free */
|
||||
info->txInterruptIndex = (info->txInterruptIndex + 1) & NUM_Tx_MASK;
|
||||
info->txSent--;
|
||||
|
||||
if (info->txSent < 0 || info->txSent > NUM_Tx_DESCR)
|
||||
dprintf("ERROR interrupt: txSent = %d\n", info->txSent);
|
||||
}
|
||||
release_spinlock(&info->txSpinlock);
|
||||
|
||||
if (releaseTxSem)
|
||||
{
|
||||
// sem_info semInfo;
|
||||
|
||||
// get_sem_info(info->txSem,&semInfo);
|
||||
release_sem_etc(info->txSem,releaseTxSem,B_DO_NOT_RESCHEDULE);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
}
|
||||
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
sis900_interrupt(void *data)
|
||||
{
|
||||
struct sis_info *info = data;
|
||||
int32 handled = B_UNHANDLED_INTERRUPT;
|
||||
int16 worklimit = 20;
|
||||
uint32 intr;
|
||||
uint32 i, j;
|
||||
cpu_status former;
|
||||
|
||||
former = disable_interrupts();
|
||||
acquire_spinlock(&info->lock);
|
||||
|
||||
while (worklimit-- > 0) {
|
||||
// reading the interrupt status register clears all interrupts
|
||||
intr = read32((uint32)info->registers + SiS900_MAC_INTR_STATUS);
|
||||
if (!intr)
|
||||
break;
|
||||
|
||||
TRACE(("************ interrupt received: %08lx **************\n", intr));
|
||||
intrCounter = (intrCounter + 1) % 100;
|
||||
lastIntr[intrCounter] = intr;
|
||||
|
||||
// wake-up event interrupt
|
||||
if (intr & SiS900_INTR_WAKEUP_EVENT)
|
||||
{
|
||||
TRACE(("wake-up event received: %ld\n", read32((uint32)info->registers + SiS900_MAC_WAKEUP_EVENT)));
|
||||
|
||||
// clear PM event register
|
||||
write32((uint32)info->registers + SiS900_MAC_WAKEUP_EVENT,
|
||||
SiS900_WAKEUP_LINK_ON | SiS900_WAKEUP_LINK_LOSS);
|
||||
handled = B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
// receive packet interrupts
|
||||
if (intr & (/*SiS900_INTR_Rx_STATUS_OVERRUN |*/ SiS900_INTR_Rx_OVERRUN |
|
||||
SiS900_INTR_Rx_ERROR | SiS900_INTR_Rx_OK))
|
||||
handled = sis900_rxInterrupt(info);
|
||||
|
||||
// transmit packet interrupts
|
||||
if (intr & (SiS900_INTR_Tx_UNDERRUN | SiS900_INTR_Tx_ERROR |
|
||||
SiS900_INTR_Tx_IDLE | SiS900_INTR_Tx_OK))
|
||||
handled = sis900_txInterrupt(info);
|
||||
}
|
||||
|
||||
release_spinlock(&info->lock);
|
||||
restore_interrupts(former);
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_disableInterrupts(struct sis_info *info)
|
||||
{
|
||||
write32((uint32)info->registers + SiS900_MAC_INTR_MASK,0);
|
||||
write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_enableInterrupts(struct sis_info *info)
|
||||
{
|
||||
write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,0);
|
||||
|
||||
// enable link detection
|
||||
write32((uint32)info->registers + SiS900_MAC_WAKEUP_CONTROL,
|
||||
SiS900_WAKEUP_LINK_ON | SiS900_WAKEUP_LINK_LOSS);
|
||||
|
||||
// set interrupt mask
|
||||
write32((uint32)info->registers + SiS900_MAC_INTR_MASK,
|
||||
//SiS900_INTR_WAKEUP_EVENT |
|
||||
SiS900_INTR_Tx_UNDERRUN | SiS900_INTR_Tx_ERROR | SiS900_INTR_Tx_IDLE | SiS900_INTR_Tx_OK |
|
||||
SiS900_INTR_Rx_STATUS_OVERRUN | SiS900_INTR_Rx_OVERRUN |
|
||||
SiS900_INTR_Rx_ERROR | SiS900_INTR_Rx_OK);
|
||||
|
||||
write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,1);
|
||||
}
|
||||
|
||||
|
||||
/***************************** PHY and link mode *****************************/
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
int32
|
||||
sis900_timer(timer *t)
|
||||
{
|
||||
struct sis_info *info = (struct sis_info *)t;
|
||||
|
||||
if (!info->autoNegotiationComplete) {
|
||||
int32 mode = sis900_readMode(info);
|
||||
if (mode)
|
||||
sis900_setMode(info, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (info->link) { // link lost
|
||||
uint16 status = mdio_status(info);
|
||||
|
||||
if ((status & MII_STATUS_LINK) == 0) {
|
||||
info->link = false;
|
||||
dprintf(DEVICE_NAME ": link lost\n");
|
||||
|
||||
// if it's the internal SiS900 MII PHY, reset it
|
||||
if (info->currentPHY->id0 == PHY_ID0_SiS900_INTERNAL
|
||||
&& (info->currentPHY->id1 & 0xfff0) == PHY_ID1_SiS900_INTERNAL)
|
||||
sis900_resetPHY(info);
|
||||
}
|
||||
}
|
||||
if (!info->link) { // new link established
|
||||
uint16 status;
|
||||
|
||||
sis900_selectPHY(info);
|
||||
|
||||
status = mdio_status(info);
|
||||
if (status & MII_STATUS_LINK) {
|
||||
sis900_checkMode(info);
|
||||
info->link = true;
|
||||
}
|
||||
}
|
||||
|
||||
// revision = info->pciInfo->revision;
|
||||
// if (!(revision == SiS900_REVISION_SiS630E || revision == SiS900_REVISION_SiS630EA1
|
||||
// || revision == SiS900_REVISION_SiS630A || revision == SiS900_REVISION_SiS630ET))
|
||||
// bug(DEVICE_NAME ": set_eq() not needed!\n");
|
||||
// else
|
||||
// bug("********* set_eq() would be needed! ********\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint16
|
||||
sis900_resetPHY(struct sis_info *info)
|
||||
{
|
||||
uint16 status = mdio_status(info);
|
||||
|
||||
mdio_write(info, MII_CONTROL, MII_CONTROL_RESET);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
sis900_initPHYs(struct sis_info *info)
|
||||
{
|
||||
uint16 phy;
|
||||
uint8 revision;
|
||||
|
||||
// search for total of 32 possible MII PHY addresses
|
||||
for (phy = 0; phy < 32; phy++) {
|
||||
struct mii_phy *mii;
|
||||
uint16 status;
|
||||
int i;
|
||||
|
||||
status = mdio_statusFromPHY(info,phy);
|
||||
if (status == 0xffff || status == 0x0000)
|
||||
// this MII is not accessable
|
||||
continue;
|
||||
|
||||
mii = (struct mii_phy *)malloc(sizeof(struct mii_phy));
|
||||
if (mii == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
mii->address = phy;
|
||||
mii->id0 = mdio_readFromPHY(info,phy,MII_PHY_ID0);
|
||||
mii->id1 = mdio_readFromPHY(info,phy,MII_PHY_ID1);
|
||||
mii->types = MII_HOME;
|
||||
mii->next = info->firstPHY;
|
||||
info->firstPHY = mii;
|
||||
|
||||
for (i = 0; gMIIChips[i].name; i++) {
|
||||
if (gMIIChips[i].id0 != mii->id0 || gMIIChips[i].id1 != (mii->id1 & 0xfff0))
|
||||
continue;
|
||||
|
||||
dprintf("Found MII PHY: %s\n", gMIIChips[i].name);
|
||||
|
||||
mii->types = gMIIChips[i].types;
|
||||
break;
|
||||
}
|
||||
if (gMIIChips[i].name == NULL)
|
||||
dprintf("Unknown MII PHY transceiver: id = (%x, %x).\n",mii->id0,mii->id1);
|
||||
}
|
||||
|
||||
if (info->firstPHY == NULL) {
|
||||
dprintf("No MII PHY transceiver found!\n");
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
sis900_selectPHY(info);
|
||||
|
||||
// if the internal PHY is selected, reset it
|
||||
if (info->currentPHY->id0 == PHY_ID0_SiS900_INTERNAL
|
||||
&& (info->currentPHY->id1 & 0xfff0) == PHY_ID1_SiS900_INTERNAL) {
|
||||
if (sis900_resetPHY(info) & MII_STATUS_LINK) {
|
||||
uint16 poll = MII_STATUS_LINK;
|
||||
while (poll) {
|
||||
poll ^= mdio_read(info,MII_STATUS) & poll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// workaround for ICS1893 PHY
|
||||
if (info->currentPHY->id0 == PHY_ID0_ICS_1893
|
||||
&& (info->currentPHY->id1 & 0xfff0) == PHY_ID1_ICS_1893)
|
||||
mdio_write(info, 0x0018, 0xD200);
|
||||
|
||||
// SiS 630E has some bugs on default value of PHY registers
|
||||
if (info->pciInfo->revision == SiS900_REVISION_SiS630E) {
|
||||
mdio_write(info, MII_AUTONEG_ADV, 0x05e1);
|
||||
mdio_write(info, MII_CONFIG1, 0x22);
|
||||
mdio_write(info, MII_CONFIG2, 0xff00);
|
||||
mdio_write(info, MII_MASK, 0xffc0);
|
||||
}
|
||||
|
||||
info->link = mdio_status(info) & MII_STATUS_LINK;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_selectPHY(struct sis_info *info)
|
||||
{
|
||||
uint16 status;
|
||||
|
||||
// ToDo: need to be changed, select PHY in relation to the link mode
|
||||
info->currentPHY = info->firstPHY;
|
||||
info->phy = info->currentPHY->address;
|
||||
|
||||
status = mdio_read(info, MII_CONTROL);
|
||||
status &= ~MII_CONTROL_ISOLATE;
|
||||
|
||||
mdio_write(info, MII_CONTROL, status);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_setMode(struct sis_info *info,int32 mode)
|
||||
{
|
||||
uint32 address = (uint32)info->registers + SiS900_MAC_CONFIG;
|
||||
uint32 txFlags = SiS900_Tx_AUTO_PADDING | SiS900_Tx_FILL_THRES;
|
||||
uint32 rxFlags = 0;
|
||||
int32 speed = mode & LINK_SPEED_MASK;
|
||||
|
||||
if (read32(address) & SiS900_MAC_CONFIG_EDB_MASTER) {
|
||||
TRACE((DEVICE_NAME ": EDB master is set!\n"));
|
||||
txFlags |= 5 << SiS900_DMA_SHIFT;
|
||||
rxFlags = 5 << SiS900_DMA_SHIFT;
|
||||
}
|
||||
|
||||
// link speed FIFO thresholds
|
||||
|
||||
if (speed == LINK_SPEED_HOME || speed == LINK_SPEED_10_MBIT) {
|
||||
rxFlags |= SiS900_Rx_10_MBIT_DRAIN_THRES;
|
||||
txFlags |= SiS900_Tx_10_MBIT_DRAIN_THRES;
|
||||
} else {
|
||||
rxFlags |= SiS900_Rx_100_MBIT_DRAIN_THRES;
|
||||
txFlags |= SiS900_Tx_100_MBIT_DRAIN_THRES;
|
||||
}
|
||||
|
||||
// duplex mode
|
||||
|
||||
if ((mode & LINK_DUPLEX_MASK) == LINK_FULL_DUPLEX) {
|
||||
txFlags |= SiS900_Tx_CS_IGNORE | SiS900_Tx_HB_IGNORE;
|
||||
rxFlags |= SiS900_Rx_ACCEPT_Tx_PACKETS;
|
||||
}
|
||||
|
||||
write32((uint32)info->registers + SiS900_MAC_Tx_CONFIG,txFlags);
|
||||
write32((uint32)info->registers + SiS900_MAC_Rx_CONFIG,rxFlags);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
sis900_readMode(struct sis_info *info)
|
||||
{
|
||||
struct mii_phy *phy = info->currentPHY;
|
||||
uint16 autoAdv, autoLinkPartner;
|
||||
int32 speed, duplex;
|
||||
|
||||
uint16 status = mdio_status(info);
|
||||
if (!(status & MII_STATUS_LINK)) {
|
||||
dprintf(DEVICE_NAME ": no link detected (status = %lx)\n", status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// auto negotiation completed
|
||||
autoAdv = mdio_read(info, MII_AUTONEG_ADV);
|
||||
autoLinkPartner = mdio_read(info, MII_AUTONEG_LINK_PARTNER);
|
||||
status = autoAdv & autoLinkPartner;
|
||||
|
||||
speed = status & (MII_NWAY_TX | MII_NWAY_TX_FDX) ? LINK_SPEED_100_MBIT : LINK_SPEED_10_MBIT;
|
||||
duplex = status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX) ? LINK_FULL_DUPLEX : LINK_HALF_DUPLEX;
|
||||
|
||||
info->autoNegotiationComplete = true;
|
||||
|
||||
// workaround for Realtek RTL8201 PHY issue
|
||||
if (phy->id0 == PHY_ID0_REALTEK_8201 && (phy->id1 & 0xFFF0) == PHY_ID1_REALTEK_8201) {
|
||||
if (mdio_read(info, MII_CONTROL) & MII_CONTROL_FULL_DUPLEX)
|
||||
duplex = LINK_FULL_DUPLEX;
|
||||
if (mdio_read(info, 0x0019) & 0x01)
|
||||
speed = LINK_SPEED_100_MBIT;
|
||||
}
|
||||
|
||||
dprintf(DEVICE_NAME ": linked, 10%s MBit, %s duplex\n",
|
||||
speed == LINK_SPEED_100_MBIT ? "0" : "",
|
||||
duplex == LINK_FULL_DUPLEX ? "full" : "half");
|
||||
|
||||
return speed | duplex;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_setAutoNegotiationCapabilities(struct sis_info *info)
|
||||
{
|
||||
uint16 status = mdio_status(info);
|
||||
uint16 capabilities = MII_NWAY_CSMA_CD
|
||||
| (status & MII_STATUS_CAN_TX_FDX ? MII_NWAY_TX_FDX : 0)
|
||||
| (status & MII_STATUS_CAN_TX ? MII_NWAY_TX : 0)
|
||||
| (status & MII_STATUS_CAN_T_FDX ? MII_NWAY_T_FDX : 0)
|
||||
| (status & MII_STATUS_CAN_T ? MII_NWAY_T : 0);
|
||||
|
||||
TRACE((DEVICE_NAME ": write capabilities %ld\n", capabilities));
|
||||
mdio_write(info, MII_AUTONEG_ADV, capabilities);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_autoNegotiate(struct sis_info *info)
|
||||
{
|
||||
uint16 status = mdio_status(info);
|
||||
int32 revision;
|
||||
int32 mode;
|
||||
|
||||
if ((status & MII_STATUS_LINK) == 0)
|
||||
{
|
||||
TRACE((DEVICE_NAME ": media link off\n"));
|
||||
info->autoNegotiationComplete = true;
|
||||
return;
|
||||
}
|
||||
TRACE((DEVICE_NAME ": auto negotiation started...\n"));
|
||||
|
||||
// reset auto negotiation
|
||||
mdio_write(info, MII_CONTROL, MII_CONTROL_AUTO | MII_CONTROL_RESET_AUTONEG);
|
||||
info->autoNegotiationComplete = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_checkMode(struct sis_info *info)
|
||||
{
|
||||
uint32 address = (uint32)info->registers + SiS900_MAC_CONFIG;
|
||||
|
||||
if (info->currentPHY->types == MII_LAN) {
|
||||
TRACE((DEVICE_NAME ": PHY type is LAN\n"));
|
||||
|
||||
// enable excessive defferal timer
|
||||
write32(address, ~SiS900_MAC_CONFIG_EXCESSIVE_DEFERRAL & read32(address));
|
||||
|
||||
sis900_setAutoNegotiationCapabilities(info);
|
||||
sis900_autoNegotiate(info);
|
||||
} else {
|
||||
TRACE((DEVICE_NAME ": PHY type is not LAN\n"));
|
||||
|
||||
// disable excessive defferal timer
|
||||
write32(address, SiS900_MAC_CONFIG_EXCESSIVE_DEFERRAL | read32(address));
|
||||
|
||||
sis900_setMode(info, LINK_SPEED_HOME | LINK_HALF_DUPLEX);
|
||||
info->autoNegotiationComplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************** MACs, rings & config *****************************/
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
bool
|
||||
sis900_getMACAddress(struct sis_info *info)
|
||||
{
|
||||
if (info->pciInfo->revision >= SiS900_REVISION_SiS96x) {
|
||||
// SiS 962 & 963 are using a different method to access the EEPROM
|
||||
// than the standard SiS 630
|
||||
long eepromAccess = (long)info->registers + SiS900_MAC_EEPROM_ACCESS;
|
||||
uint32 tries = 0;
|
||||
|
||||
write32(eepromAccess, SiS96x_EEPROM_CMD_REQ);
|
||||
|
||||
while (tries < 1000) {
|
||||
if (read32(eepromAccess) & SiS96x_EEPROM_CMD_GRANT) {
|
||||
int i;
|
||||
|
||||
/* get MAC address from EEPROM */
|
||||
for (i = 0; i < 3; i++) {
|
||||
uint16 id;
|
||||
|
||||
id = eeprom_read(info, SiS900_EEPROM_MAC_ADDRESS + i);
|
||||
info->address.ebyte[i*2 + 1] = id >> 8;
|
||||
info->address.ebyte[i*2] = id & 0xff;
|
||||
}
|
||||
|
||||
write32(eepromAccess, SiS96x_EEPROM_CMD_DONE);
|
||||
return true;
|
||||
} else {
|
||||
spin(2);
|
||||
tries++;
|
||||
}
|
||||
}
|
||||
write32(eepromAccess, SiS96x_EEPROM_CMD_DONE);
|
||||
return false;
|
||||
} else if (info->pciInfo->revision >= SiS900_REVISION_SiS630E) {
|
||||
/* SiS630E and above are using CMOS RAM to store MAC address */
|
||||
pci_info isa;
|
||||
int32 index;
|
||||
|
||||
for (index = 0; pci->get_nth_pci_info(index, &isa) == B_OK; index++) {
|
||||
if (isa.vendor_id == VENDOR_ID_SiS && isa.device_id == DEVICE_ID_SiS_ISA_BRIDGE)
|
||||
{
|
||||
uint8 reg,i;
|
||||
uint32 registers = isa.u.h0.base_registers[0];
|
||||
|
||||
reg = pci->read_pci_config(isa.bus,isa.device,isa.function,0x48,1);
|
||||
pci->write_pci_config(isa.bus,isa.device,isa.function,0x48,1,reg | 0x40);
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
write8(registers + 0x70,0x09 + i);
|
||||
info->address.ebyte[i] = read8(registers + 0x71);
|
||||
}
|
||||
|
||||
pci->write_pci_config(isa.bus,isa.device,isa.function,0x48,1,reg & ~0x40);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
/* SiS630 stores the MAC in an eeprom */
|
||||
uint16 signature;
|
||||
int i;
|
||||
|
||||
/* check to see if we have sane EEPROM */
|
||||
signature = eeprom_read(info,SiS900_EEPROM_SIGNATURE);
|
||||
if (signature == 0xffff || signature == 0x0000) {
|
||||
dprintf(DEVICE_NAME ": cannot read EEPROM signature\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get MAC address from EEPROM */
|
||||
for (i = 0; i < 3; i++) {
|
||||
uint16 id;
|
||||
|
||||
id = eeprom_read(info,SiS900_EEPROM_MAC_ADDRESS + i);
|
||||
info->address.ebyte[i*2 + 1] = id >> 8;
|
||||
info->address.ebyte[i*2] = id & 0xff;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
sis900_reset(struct sis_info *info)
|
||||
{
|
||||
uint32 address = (uint32)info->registers + SiS900_MAC_COMMAND;
|
||||
int16 tries = 1000;
|
||||
status_t status;
|
||||
|
||||
TRACE(("sis900 reset\n"));
|
||||
|
||||
//write32((uint32)info->registers + SiS900_MAC_INTR_MASK,0);
|
||||
//write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,0);
|
||||
|
||||
write32(address, SiS900_MAC_CMD_RESET);
|
||||
|
||||
write32((uint32)info->registers + SiS900_MAC_Rx_FILTER_CONTROL, SiS900_RxF_ENABLE |
|
||||
SiS900_RxF_ACCEPT_ALL_BROADCAST | SiS900_RxF_ACCEPT_ALL_MULTICAST);
|
||||
|
||||
// wait until the chip leaves reset state
|
||||
while ((read32(address) & SiS900_MAC_CMD_RESET) && tries-- > 0)
|
||||
snooze(2);
|
||||
|
||||
write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_setPromiscuous(struct sis_info *info, bool on)
|
||||
{
|
||||
long filterControl = (long)info->registers + SiS900_MAC_Rx_FILTER_CONTROL;
|
||||
int32 filter = read32(filterControl);
|
||||
|
||||
// accept all incoming packets (or not)
|
||||
if (on) {
|
||||
write32(filterControl, SiS900_RxF_ENABLE |
|
||||
SiS900_RxF_ACCEPT_ALL_BROADCAST | SiS900_RxF_ACCEPT_ALL_MULTICAST);
|
||||
} else
|
||||
write32(filterControl, filter | ~SiS900_RxF_ACCEPT_ALL_ADDRESSES);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_setRxFilter(struct sis_info *info)
|
||||
{
|
||||
long filterControl = (long)info->registers + SiS900_MAC_Rx_FILTER_CONTROL;
|
||||
long filterData = (long)info->registers + SiS900_MAC_Rx_FILTER_DATA;
|
||||
int i;
|
||||
|
||||
// set MAC address as receive filter
|
||||
for (i = 0; i < 3; i++) {
|
||||
write32(filterControl, i << SiS900_Rx_FILTER_ADDRESS_SHIFT);
|
||||
write32(filterData, info->address.ebyte[i*2] | (info->address.ebyte[i*2 + 1] << 8));
|
||||
}
|
||||
write32(filterControl, SiS900_RxF_ENABLE |
|
||||
SiS900_RxF_ACCEPT_ALL_BROADCAST | SiS900_RxF_ACCEPT_ALL_MULTICAST /*| SiS900_RxF_ACCEPT_ALL_ADDRESSES*/);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sis900_deleteRings(struct sis_info *info)
|
||||
{
|
||||
delete_area(info->txArea);
|
||||
delete_area(info->rxArea);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
sis900_createRings(struct sis_info *info)
|
||||
{
|
||||
uint16 i;
|
||||
|
||||
// create transmit buffer area
|
||||
if ((info->txArea = create_area("sis900 tx buffer", (void **)&info->txBuffer[0],
|
||||
B_ANY_KERNEL_ADDRESS,
|
||||
ROUND_TO_PAGE_SIZE(BUFFER_SIZE * NUM_Tx_DESCR),
|
||||
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK)
|
||||
return info->txArea;
|
||||
|
||||
// initialize transmit buffer descriptors
|
||||
for (i = 1; i < NUM_Tx_DESCR; i++)
|
||||
info->txBuffer[i] = (void *)(((uint32)info->txBuffer[0]) + (i * BUFFER_SIZE));
|
||||
|
||||
for (i = 0; i < NUM_Tx_DESCR; i++) {
|
||||
info->txDescriptor[i].status = 0;
|
||||
info->txDescriptor[i].buffer = physicalAddress(info->txBuffer[i], BUFFER_SIZE);
|
||||
info->txDescriptor[i].link =
|
||||
#if (NUM_Tx_DESCR == 1)
|
||||
0;
|
||||
#else
|
||||
physicalAddress(&info->txDescriptor[(i + 1) & NUM_Tx_MASK], sizeof(struct buffer_desc));
|
||||
#endif
|
||||
}
|
||||
|
||||
// create receive buffer area
|
||||
if ((info->rxArea = create_area("sis900 rx buffer", (void **)&info->rxBuffer[0],
|
||||
B_ANY_KERNEL_ADDRESS,
|
||||
ROUND_TO_PAGE_SIZE(BUFFER_SIZE * NUM_Rx_DESCR),
|
||||
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK)
|
||||
{
|
||||
delete_area(info->txArea);
|
||||
return info->rxArea;
|
||||
}
|
||||
|
||||
// initialize receive buffer descriptors
|
||||
for (i = 1; i < NUM_Rx_DESCR; i++)
|
||||
info->rxBuffer[i] = (void *)(((uint32)info->rxBuffer[0]) + (i * BUFFER_SIZE));
|
||||
|
||||
for (i = 0; i < NUM_Rx_DESCR; i++) {
|
||||
info->rxDescriptor[i].status = MAX_FRAME_SIZE;
|
||||
info->rxDescriptor[i].buffer = physicalAddress(info->rxBuffer[i],BUFFER_SIZE);
|
||||
info->rxDescriptor[i].link = physicalAddress(&info->rxDescriptor[(i + 1) & NUM_Rx_MASK],sizeof(struct buffer_desc));
|
||||
}
|
||||
info->rxFree = NUM_Rx_DESCR;
|
||||
|
||||
// set descriptor pointer registers
|
||||
write32((uint32)info->registers + SiS900_MAC_Tx_DESCR,physicalAddress(&info->txDescriptor[0],sizeof(struct buffer_desc)));
|
||||
write32((uint32)info->registers + SiS900_MAC_Rx_DESCR,physicalAddress(&info->rxDescriptor[0],sizeof(struct buffer_desc)));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
387
src/add-ons/kernel/drivers/network/sis900/sis900.h
Normal file
387
src/add-ons/kernel/drivers/network/sis900/sis900.h
Normal file
@ -0,0 +1,387 @@
|
||||
#ifndef SIS900_H
|
||||
#define SIS900_H
|
||||
/* sis900.h - SiS 900 driver/chip specific definitions
|
||||
**
|
||||
** Copyright © 2001-2003 pinc Software. All Rights Reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include "ether_driver.h"
|
||||
|
||||
#define VENDOR_ID_SiS 0x1039
|
||||
#define DEVICE_ID_SiS900 0x900
|
||||
|
||||
#define DEVICE_ID_SiS_ISA_BRIDGE 0x0008
|
||||
|
||||
enum SiS_revisions {
|
||||
SiS900_REVISION_B = 0x03,
|
||||
SiS900_REVISION_SiS630A = 0x80,
|
||||
SiS900_REVISION_SiS630E = 0x81,
|
||||
SiS900_REVISION_SiS630S = 0x82,
|
||||
SiS900_REVISION_SiS630EA1 = 0x83,
|
||||
SiS900_REVISION_SiS630ET = 0x84,
|
||||
SiS900_REVISION_SiS635A = 0x90,
|
||||
SiS900_REVISION_SiS96x = 0x91,
|
||||
};
|
||||
|
||||
#define DEVICE_NAME "sis900"
|
||||
#define DEVICE_DRIVERNAME "net/" DEVICE_NAME
|
||||
|
||||
|
||||
/***************************** Buffer & Buffer Descriptors *****************************/
|
||||
|
||||
struct buffer_desc {
|
||||
uint32 link;
|
||||
uint32 status;
|
||||
uint32 buffer;
|
||||
};
|
||||
|
||||
enum SiS900_buffer_desc_bits {
|
||||
SiS900_DESCR_OWN = 0x80000000,
|
||||
SiS900_DESCR_MORE = 0x40000000,
|
||||
SiS900_DESCR_INTR = 0x20000000,
|
||||
SiS900_DESCR_SUPPRESS_CRC = 0x10000000,
|
||||
SiS900_DESCR_OK = 0x08000000,
|
||||
|
||||
SiS900_DESCR_Tx_ABORT = 0x04000000,
|
||||
SiS900_DESCR_Tx_UNDERRUN = 0x02000000,
|
||||
SiS900_DESCR_Tx_CRS = 0x01000000,
|
||||
SiS900_DESCR_Tx_DEFERRED = 0x00800000,
|
||||
SiS900_DESCR_Tx_EXC_DEFERRED = 0x00400000,
|
||||
SiS900_DESCR_Tx_OOW_COLLISION = 0x00200000,
|
||||
SiS900_DESCR_Tx_EXC_COLLISIONS = 0x00100000,
|
||||
|
||||
SiS900_DESCR_Rx_ABORT = 0x04000000,
|
||||
SiS900_DESCR_Rx_OVERRUN = 0x02000000,
|
||||
SiS900_DESCR_Rx_DESTCLASS0 = 0x01000000,
|
||||
SiS900_DESCR_Rx_DESTCLASS1 = 0x00800000,
|
||||
SiS900_DESCR_Rx_LONG_PACKET = 0x00400000,
|
||||
SiS900_DESCR_Rx_RUNT_PACKET = 0x00200000,
|
||||
SiS900_DESCR_Rx_INVALID_SYM = 0x00100000,
|
||||
SiS900_DESCR_Rx_CRC_ERROR = 0x00080000,
|
||||
SiS900_DESCR_Rx_FRAME_ALIGN = 0x00040000,
|
||||
SiS900_DESCR_Rx_LOOPBACK = 0x00020000,
|
||||
SiS900_DESCR_Rx_COLLISION = 0x00010000,
|
||||
};
|
||||
|
||||
#define SiS900_DESCR_SIZE_MASK 0x0fff
|
||||
|
||||
#define NUM_Rx_DESCR 32
|
||||
#define NUM_Tx_DESCR 16
|
||||
|
||||
#define NUM_Rx_MASK (NUM_Rx_DESCR - 1)
|
||||
#define NUM_Tx_MASK (NUM_Tx_DESCR - 1)
|
||||
|
||||
|
||||
#define MAX_FRAME_SIZE 1514
|
||||
#define BUFFER_SIZE 2048
|
||||
#define CRC_SIZE 4
|
||||
|
||||
|
||||
/***************************** Private Device Data *****************************/
|
||||
|
||||
struct sis_info {
|
||||
timer timer;
|
||||
uint32 cookieMagic;
|
||||
area_id thisArea;
|
||||
uint32 id;
|
||||
spinlock lock;
|
||||
pci_info *pciInfo;
|
||||
volatile unsigned char *registers;
|
||||
ether_address_t address;
|
||||
volatile int32 blockFlag;
|
||||
|
||||
struct mii_phy *firstPHY;
|
||||
struct mii_phy *currentPHY;
|
||||
uint16 phy;
|
||||
bool autoNegotiationComplete;
|
||||
bool link;
|
||||
|
||||
// receive data
|
||||
volatile struct buffer_desc rxDescriptor[NUM_Rx_DESCR];
|
||||
volatile char *rxBuffer[NUM_Rx_DESCR];
|
||||
area_id rxArea;
|
||||
sem_id rxSem;
|
||||
spinlock rxSpinlock;
|
||||
int32 rxLock;
|
||||
int16 rxCurrent;
|
||||
int16 rxInterruptIndex;
|
||||
int16 rxFree;
|
||||
|
||||
// transmit data
|
||||
volatile struct buffer_desc txDescriptor[NUM_Tx_DESCR];
|
||||
volatile char *txBuffer[NUM_Tx_DESCR];
|
||||
area_id txArea;
|
||||
sem_id txSem;
|
||||
spinlock txSpinlock;
|
||||
int32 txLock;
|
||||
int16 txCurrent;
|
||||
int16 txInterruptIndex;
|
||||
int16 txSent;
|
||||
};
|
||||
|
||||
#define SiS_COOKIE_MAGIC 's900'
|
||||
#define SiS_FREE_COOKIE_MAGIC 's9fr'
|
||||
|
||||
enum link_modes {
|
||||
LINK_HALF_DUPLEX = 0x0100,
|
||||
LINK_FULL_DUPLEX = 0x0200,
|
||||
LINK_DUPLEX_MASK = 0xff00,
|
||||
|
||||
LINK_SPEED_HOME = 1,
|
||||
LINK_SPEED_10_MBIT = 10,
|
||||
LINK_SPEED_100_MBIT = 100,
|
||||
LINK_SPEED_DEFAULT = LINK_SPEED_100_MBIT,
|
||||
LINK_SPEED_MASK = 0x00ff
|
||||
};
|
||||
|
||||
/***************************** Media Access Control *****************************/
|
||||
|
||||
enum SiS900_MAC_registers {
|
||||
SiS900_MAC_COMMAND = 0x00,
|
||||
SiS900_MAC_CONFIG = 0x04,
|
||||
SiS900_MAC_EEPROM_ACCESS = 0x08,
|
||||
SiS900_MAC_PCI_TEST = 0x0c,
|
||||
SiS900_MAC_INTR_STATUS = 0x10,
|
||||
SiS900_MAC_INTR_MASK = 0x14,
|
||||
SiS900_MAC_INTR_ENABLE = 0x18,
|
||||
SiS900_MAC_PHY_ACCESS = 0x1c,
|
||||
SiS900_MAC_Tx_DESCR = 0x20,
|
||||
SiS900_MAC_Tx_CONFIG = 0x24,
|
||||
/* reserved 0x28 - 0x2c */
|
||||
SiS900_MAC_Rx_DESCR = 0x30,
|
||||
SiS900_MAC_Rx_CONFIG = 0x34,
|
||||
SiS900_MAC_FLOW_CONTROL = 0x38,
|
||||
/* reserved 0x3c - 0x44 */
|
||||
SiS900_MAC_Rx_FILTER_CONTROL = 0x48,
|
||||
SiS900_MAC_Rx_FILTER_DATA = 0x4c,
|
||||
/* reserved 0x50 - 0xac */
|
||||
SiS900_MAC_WAKEUP_CONTROL = 0xb0,
|
||||
SiS900_MAC_WAKEUP_EVENT = 0xb4
|
||||
/* reserved, wake-up registers */
|
||||
};
|
||||
|
||||
enum SiS900_MAC_commands {
|
||||
SiS900_MAC_CMD_RESET = 0x0100,
|
||||
SiS900_MAC_CMD_Rx_RESET = 0x0020,
|
||||
SiS900_MAC_CMD_Tx_RESET = 0x0010,
|
||||
SiS900_MAC_CMD_Rx_DISABLE = 0x0008,
|
||||
SiS900_MAC_CMD_Rx_ENABLE = 0x0004,
|
||||
SiS900_MAC_CMD_Tx_DISABLE = 0x0002,
|
||||
SiS900_MAC_CMD_Tx_ENABLE = 0x0001
|
||||
};
|
||||
|
||||
enum SiS900_MAC_configuration_bits {
|
||||
SiS900_MAC_CONFIG_BIG_ENDIAN = 0x00000001,
|
||||
SiS900_MAC_CONFIG_EXCESSIVE_DEFERRAL = 0x00000010,
|
||||
SiS900_MAC_CONFIG_EDB_MASTER = 0x00002000,
|
||||
};
|
||||
|
||||
enum SiS900_MAC_rxfilter {
|
||||
SiS900_RxF_ENABLE = 0x80000000,
|
||||
SiS900_RxF_ACCEPT_ALL_BROADCAST = 0x40000000,
|
||||
SiS900_RxF_ACCEPT_ALL_MULTICAST = 0x20000000,
|
||||
SiS900_RxF_ACCEPT_ALL_ADDRESSES = 0x10000000
|
||||
};
|
||||
|
||||
#define SiS900_Rx_FILTER_ADDRESS_SHIFT 16
|
||||
|
||||
enum SiS900_MAC_txconfig {
|
||||
SiS900_Tx_CS_IGNORE = 0x80000000,
|
||||
SiS900_Tx_HB_IGNORE = 0x40000000,
|
||||
SiS900_Tx_MAC_LOOPBACK = 0x20000000,
|
||||
SiS900_Tx_AUTO_PADDING = 0x10000000
|
||||
};
|
||||
|
||||
#define SiS900_DMA_SHIFT 20
|
||||
#define SiS900_Tx_FILL_THRES (16 << 8) /* 1/4 of FIFO */
|
||||
#define SiS900_Tx_100_MBIT_DRAIN_THRES 48 /* 3/4 */
|
||||
#define SiS900_Tx_10_MBIT_DRAIN_THRES 16 // 32 /* 1/2 */
|
||||
|
||||
enum SiS900_MAC_rxconfig {
|
||||
SiS900_Rx_ACCEPT_ERROR_PACKETS = 0x80000000,
|
||||
SiS900_Rx_ACCEPT_RUNT_PACKETS = 0x40000000,
|
||||
SiS900_Rx_ACCEPT_Tx_PACKETS = 0x10000000,
|
||||
SiS900_Rx_ACCEPT_JABBER_PACKETS = 0x08000000
|
||||
};
|
||||
|
||||
#define SiS900_Rx_100_MBIT_DRAIN_THRES 32 /* 1/2 of FIFO */
|
||||
#define SiS900_Rx_10_MBIT_DRAIN_THRES 48 /* 3/4 */
|
||||
|
||||
enum SiS900_MAC_wakeup {
|
||||
SiS900_WAKEUP_LINK_ON = 0x02,
|
||||
SiS900_WAKEUP_LINK_LOSS = 0x01
|
||||
};
|
||||
|
||||
enum SiS900_MAC_interrupts {
|
||||
SiS900_INTR_WAKEUP_EVENT = 0x10000000,
|
||||
SiS900_INTR_Tx_RESET_COMPLETE = 0x02000000,
|
||||
SiS900_INTR_Rx_RESET_COMPLETE = 0x01000000,
|
||||
SiS900_INTR_Rx_STATUS_OVERRUN = 0x00010000,
|
||||
SiS900_INTR_Tx_UNDERRUN = 0x00000400,
|
||||
SiS900_INTR_Tx_IDLE = 0x00000200,
|
||||
SiS900_INTR_Tx_ERROR = 0x00000100,
|
||||
SiS900_INTR_Tx_DESCRIPTION = 0x00000080,
|
||||
SiS900_INTR_Tx_OK = 0x00000040,
|
||||
SiS900_INTR_Rx_OVERRUN = 0x00000020,
|
||||
SiS900_INTR_Rx_IDLE = 0x00000010,
|
||||
SiS900_INTR_Rx_EARLY_THRESHOLD = 0x00000008,
|
||||
SiS900_INTR_Rx_ERROR = 0x00000004,
|
||||
SiS900_INTR_Rx_DESCRIPTION = 0x00000002,
|
||||
SiS900_INTR_Rx_OK = 0x00000001
|
||||
};
|
||||
|
||||
/***************************** EEPROM *****************************/
|
||||
|
||||
enum SiS900_EEPROM_bits {
|
||||
SiS900_EEPROM_SELECT = 0x08,
|
||||
SiS900_EEPROM_CLOCK = 0x04,
|
||||
SiS900_EEPROM_DATA_OUT = 0x02,
|
||||
SiS900_EEPROM_DATA_IN = 0x01
|
||||
};
|
||||
|
||||
// the EEPROM definitions are taken from linux' sis900 driver header
|
||||
enum SiS900_EEPROM_address {
|
||||
SiS900_EEPROM_SIGNATURE = 0x00,
|
||||
SiS900_EEPROM_VENDOR_ID = 0x02,
|
||||
SiS900_EEPROM_DEVICE_ID = 0x03,
|
||||
SiS900_EEPROM_MAC_ADDRESS = 0x08,
|
||||
SiS900_EEPROM_CHECKSUM = 0x0b
|
||||
};
|
||||
|
||||
enum SiS900_EEPROM_commands {
|
||||
SiS900_EEPROM_CMD_READ = 0x0180
|
||||
};
|
||||
|
||||
enum SiS96x_EEPROM_command {
|
||||
SiS96x_EEPROM_CMD_REQ = 0x00000400,
|
||||
SiS96x_EEPROM_CMD_DONE = 0x00000200,
|
||||
SiS96x_EEPROM_CMD_GRANT = 0x00000100
|
||||
};
|
||||
|
||||
/***************************** Media Independent Interface *****************************/
|
||||
|
||||
struct mii_phy {
|
||||
struct mii_phy *next;
|
||||
uint16 id0,id1;
|
||||
uint16 address;
|
||||
uint8 types;
|
||||
};
|
||||
|
||||
enum SiS900_MII_bits {
|
||||
// SiS900_MII_WRITE = 0x00,
|
||||
// SiS900_MII_READ = 0x20,
|
||||
// SiS900_MII_ACCESS = 0x10
|
||||
SiS900_MII_MDC = 0x40,
|
||||
SiS900_MII_MDDIR = 0x20,
|
||||
SiS900_MII_MDIO = 0x10,
|
||||
};
|
||||
|
||||
enum SiS900_MII_address {
|
||||
// standard registers
|
||||
MII_CONTROL = 0x00,
|
||||
MII_STATUS = 0x01,
|
||||
MII_PHY_ID0 = 0x02,
|
||||
MII_PHY_ID1 = 0x03,
|
||||
MII_AUTONEG_ADV = 0x04,
|
||||
MII_AUTONEG_LINK_PARTNER = 0x05,
|
||||
MII_AUTONEG_EXT = 0x06,
|
||||
|
||||
// SiS900 specific registers
|
||||
MII_CONFIG1 = 0x10,
|
||||
MII_CONFIG2 = 0x11,
|
||||
MII_LINK_STATUS = 0x12,
|
||||
MII_MASK = 0x13,
|
||||
MII_RESERVED = 0x14
|
||||
};
|
||||
|
||||
enum MII_control {
|
||||
MII_CONTROL_RESET = 0x8000,
|
||||
MII_CONTROL_RESET_AUTONEG = 0x0200,
|
||||
MII_CONTROL_AUTO = 0x1000,
|
||||
MII_CONTROL_FULL_DUPLEX = 0x0100,
|
||||
MII_CONTROL_ISOLATE = 0x0400
|
||||
};
|
||||
|
||||
enum SiS900_MII_commands {
|
||||
MII_CMD_READ = 0x6000,
|
||||
MII_CMD_WRITE = 0x5002,
|
||||
|
||||
MII_PHY_SHIFT = 7,
|
||||
MII_REG_SHIFT = 2,
|
||||
};
|
||||
|
||||
enum MII_status_bits {
|
||||
MII_STATUS_EXT = 0x0001,
|
||||
MII_STATUS_JAB = 0x0002,
|
||||
MII_STATUS_LINK = 0x0004,
|
||||
MII_STATUS_CAN_AUTO = 0x0008,
|
||||
MII_STATUS_FAULT = 0x0010,
|
||||
MII_STATUS_AUTO_DONE = 0x0020,
|
||||
MII_STATUS_CAN_T = 0x0800,
|
||||
MII_STATUS_CAN_T_FDX = 0x1000,
|
||||
MII_STATUS_CAN_TX = 0x2000,
|
||||
MII_STATUS_CAN_TX_FDX = 0x4000,
|
||||
MII_STATUS_CAN_T4 = 0x8000
|
||||
};
|
||||
|
||||
enum MII_auto_negotiation {
|
||||
// taken from the linux sis900.h header file
|
||||
MII_NWAY_NODE_SEL = 0x001f,
|
||||
MII_NWAY_CSMA_CD = 0x0001,
|
||||
MII_NWAY_T = 0x0020,
|
||||
MII_NWAY_T_FDX = 0x0040,
|
||||
MII_NWAY_TX = 0x0080,
|
||||
MII_NWAY_TX_FDX = 0x0100,
|
||||
MII_NWAY_T4 = 0x0200,
|
||||
MII_NWAY_PAUSE = 0x0400,
|
||||
MII_NWAY_RF = 0x2000,
|
||||
MII_NWAY_ACK = 0x4000,
|
||||
MII_NWAY_NP = 0x8000
|
||||
};
|
||||
|
||||
|
||||
enum SiS900_MII_link_status {
|
||||
MII_LINK_FAIL = 0x4000,
|
||||
MII_LINK_100_MBIT = 0x0080,
|
||||
MII_LINK_FULL_DUPLEX = 0x0040
|
||||
};
|
||||
|
||||
#define SiS900_MII_REGISTER_SHIFT 6
|
||||
#define SiS900_MII_DATA_SHIFT 16
|
||||
|
||||
|
||||
/***************************** Misc. & Prototypes *****************************/
|
||||
|
||||
#define HACK(x) ;
|
||||
|
||||
#ifdef EXCESSIVE_DEBUG
|
||||
# define TRACE(x) dprintf x
|
||||
extern int bug(const char *, ...);
|
||||
#elif defined(DEBUG)
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
|
||||
|
||||
// chip specific network functions
|
||||
extern int32 sis900_interrupt(void *data);
|
||||
extern void sis900_disableInterrupts(struct sis_info *info);
|
||||
extern void sis900_enableInterrupts(struct sis_info *info);
|
||||
|
||||
extern int32 sis900_timer(timer *t);
|
||||
extern status_t sis900_initPHYs(struct sis_info *info);
|
||||
extern void sis900_checkMode(struct sis_info *info);
|
||||
extern bool sis900_getMACAddress(struct sis_info *info);
|
||||
extern status_t sis900_reset(struct sis_info *info);
|
||||
|
||||
extern void sis900_setPromiscuous(struct sis_info *info, bool on);
|
||||
extern void sis900_setRxFilter(struct sis_info *info);
|
||||
|
||||
extern void sis900_deleteRings(struct sis_info *info);
|
||||
extern status_t sis900_createRings(struct sis_info *info);
|
||||
|
||||
|
||||
#endif /* SIS900_H */
|
Loading…
Reference in New Issue
Block a user