/* Copyright (c) 2003-2011
* Stefano Ceccherini <stefano.ceccherini@gmail.com>. All rights reserved.
* This file is released under the MIT license
*/
#include <KernelExport.h>
#include <Errors.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <net/if_media.h>
#include "debug.h"
#include "device.h"
#include "driver.h"
#include "interface.h"
#include "wb840.h"
#define MAX_CARDS 4
extern char* gDevNameList[];
extern pci_info* gDevList[];
static int32 sOpenMask = 0;
static status_t
wb840_open(const char* name, uint32 flags, void** cookie)
{
char* deviceName = NULL;
int32 i;
int32 mask;
struct wb_device* data;
status_t status;
LOG((DEVICE_NAME ": open()\n"));
for (i = 0; (deviceName = gDevNameList[i]) != NULL; i++) {
if (!strcmp(name, deviceName))
break;
}
if (deviceName == NULL) {
LOG(("invalid device name"));
return EINVAL;
}
// There can be only one access at time
mask = 1L << i;
if (atomic_or(&sOpenMask, mask) & mask)
return B_BUSY;
// Allocate a wb_device structure
if (!(data = (wb_device*)calloc(1, sizeof(wb_device)))) {
sOpenMask &= ~(1L << i);
return B_NO_MEMORY;
}
*cookie = data;
#ifdef DEBUG
load_driver_symbols("wb840");
#endif
data->devId = i;
data->pciInfo = gDevList[i];
data->deviceName = gDevNameList[i];
data->blockFlag = 0;
data->reg_base = data->pciInfo->u.h0.base_registers[0];
data->wb_cachesize = gPci->read_pci_config(data->pciInfo->bus,
data->pciInfo->device, data->pciInfo->function, PCI_line_size,
sizeof(PCI_line_size)) & 0xff;
wb_read_eeprom(data, &data->MAC_Address, 0, 3, false);
status = wb_create_semaphores(data);
if (status < B_OK) {
LOG((DEVICE_NAME": couldn't create semaphores\n"));
goto err;
}
status = wb_stop(data);
if (status < B_OK) {
LOG((DEVICE_NAME": can't stop device\n"));
goto err1;
}
status = wb_initPHYs(data);
if (status < B_OK) {
LOG((DEVICE_NAME": can't init PHYs\n"));
goto err1;
}
wb_init(data);
/* Setup interrupts */
data->irq = data->pciInfo->u.h0.interrupt_line;
status = install_io_interrupt_handler(data->irq, wb_interrupt, data, 0);
if (status < B_OK) {
LOG((DEVICE_NAME
": can't install interrupt handler: %s\n", strerror(status)));
goto err1;
}
LOG((DEVICE_NAME ": interrupts installed at irq line %x\n", data->irq));
status = wb_create_rings(data);
if (status < B_OK) {
LOG((DEVICE_NAME": can't create ring buffers\n"));
goto err2;
}
wb_enable_interrupts(data);
WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_RX_ON);
write32(data->reg_base + WB_RXSTART, 0xFFFFFFFF);
WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_TX_ON);
add_timer(&data->timer, wb_tick, 1000000LL, B_PERIODIC_TIMER);
return B_OK; // Everything after this line is an error
err2:
remove_io_interrupt_handler(data->irq, wb_interrupt, data);
err1:
wb_delete_semaphores(data);
err:
sOpenMask &= ~(1L << i);
free(data);
LOG(("wb840: Open Failed\n"));
return status;
}
static status_t
wb840_read(void* cookie, off_t position, void* buf, size_t* num_bytes)
{
wb_device* device = (wb_device*)cookie;
int16 current;
status_t status;
size_t size;
int32 blockFlag;
uint32 check;
LOG((DEVICE_NAME ": read()\n"));
blockFlag = device->blockFlag;
if (atomic_or(&device->rxLock, 1)) {
*num_bytes = 0;
return B_ERROR;
}
status = acquire_sem_etc(device->rxSem, 1, B_CAN_INTERRUPT | blockFlag, 0);
if (status < B_OK) {
atomic_and(&device->rxLock, 0);
*num_bytes = 0;
return status;
}
current = device->rxCurrent;
check = device->rxDescriptor[current].wb_status;
if (check & WB_RXSTAT_OWN) {
LOG((DEVICE_NAME ":ERROR: read: buffer %d still in use: %x\n",
(int)current, (int)status));
atomic_and(&device->rxLock, 0);
*num_bytes = 0;
return B_BUSY;
}
if (check & (WB_RXSTAT_RXERR | WB_RXSTAT_CRCERR | WB_RXSTAT_RUNT)) {
LOG(("Error read: packet with errors."));
*num_bytes = 0;
} else {
size = WB_RXBYTES(check);
size -= CRC_SIZE;
LOG((DEVICE_NAME": received %ld bytes\n", size));
if (size > WB_MAX_FRAMELEN || size > *num_bytes) {
LOG(("ERROR: Bad frame size: %ld", size));
size = *num_bytes;
}
*num_bytes = size;
memcpy(buf, (void*)device->rxBuffer[current], size);
}
device->rxCurrent = (current + 1) & WB_RX_CNT_MASK;
{
cpu_status former;
former = disable_interrupts();
acquire_spinlock(&device->rxSpinlock);
// release buffer to ring
wb_put_rx_descriptor(&device->rxDescriptor[current]);
device->rxFree++;
release_spinlock(&device->rxSpinlock);
restore_interrupts(former);
}
atomic_and(&device->rxLock, 0);
return B_OK;
}
static status_t
wb840_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
{
wb_device* device = (wb_device*)cookie;
status_t status = B_OK;
uint16 frameSize;
int16 current;
uint32 check;
LOG((DEVICE_NAME ": write()\n"));
atomic_add(&device->txLock, 1);
if (*num_bytes > WB_MAX_FRAMELEN)
*num_bytes = WB_MAX_FRAMELEN;
frameSize = *num_bytes;
current = device->txCurrent;
// block until a free tx descriptor is available
status = acquire_sem_etc(device->txSem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT);
if (status < B_OK) {
write32(device->reg_base + WB_TXSTART, 0xFFFFFFFF);
LOG((DEVICE_NAME": write: acquiring sem failed: %ld, %s\n",
status, strerror(status)));
atomic_add(&device->txLock, -1);
*num_bytes = 0;
return status;
}
check = device->txDescriptor[current].wb_status;
if (check & WB_TXSTAT_OWN) {
// descriptor is still in use
dprintf(DEVICE_NAME ": card owns buffer %d\n", (int)current);
atomic_add(&device->txLock, -1);
*num_bytes = 0;
return B_ERROR;
}
/* Copy data to tx buffer */
memcpy((void*)device->txBuffer[current], buffer, frameSize);
device->txCurrent = (current + 1) & WB_TX_CNT_MASK;
LOG((DEVICE_NAME ": %d bytes written\n", frameSize));
{
cpu_status former = disable_interrupts();
acquire_spinlock(&device->txSpinlock);
device->txDescriptor[current].wb_ctl = WB_TXCTL_TLINK | frameSize;
device->txDescriptor[current].wb_ctl |= WB_TXCTL_FIRSTFRAG
| WB_TXCTL_LASTFRAG;
device->txDescriptor[current].wb_status = WB_TXSTAT_OWN;
device->txSent++;
release_spinlock(&device->txSpinlock);
restore_interrupts(former);
}
// re-enable transmit state machine
write32(device->reg_base + WB_TXSTART, 0xFFFFFFFF);
atomic_add(&device->txLock, -1);
return B_OK;
}
static status_t
wb840_control (void* cookie, uint32 op, void* arg, size_t len)
{
wb_device* data = (wb_device*)cookie;
LOG((DEVICE_NAME ": control()\n"));
switch (op) {
case ETHER_INIT:
LOG(("%s: ETHER_INIT\n", data->deviceName));
return B_OK;
case ETHER_GETADDR:
LOG(("%s: ETHER_GETADDR\n", data->deviceName));
memcpy(arg, &data->MAC_Address, sizeof(data->MAC_Address));
print_address(arg);
return B_OK;
case ETHER_NONBLOCK:
LOG(("ETHER_NON_BLOCK\n"));
data->blockFlag = *(int32*)arg ? B_TIMEOUT : 0;
return B_OK;
case ETHER_GETFRAMESIZE:
LOG(("ETHER_GETFRAMESIZE\n"));
*(uint32 *)arg = WB_MAX_FRAMELEN;
return B_OK;
case ETHER_GET_LINK_STATE:
{
ether_link_state_t state;
LOG(("ETHER_GET_LINK_STATE"));
state.media = (data->link ? IFM_ACTIVE : 0) | IFM_ETHER
| (data->full_duplex ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX)
| (data->speed == LINK_SPEED_100_MBIT ? IFM_100_TX : IFM_10_T);
state.speed = data->speed == LINK_SPEED_100_MBIT
? 100000000 : 10000000;
state.quality = 1000;
return user_memcpy(arg, &state, sizeof(ether_link_state_t));
}
case ETHER_ADDMULTI:
LOG(("ETHER_ADDMULTI\n"));
break;
case ETHER_REMMULTI:
LOG(("ETHER_REMMULTI\n"));
break;
case ETHER_SETPROMISC:
LOG(("ETHER_SETPROMISC\n"));
break;
default:
LOG(("Invalid command\n"));
break;
}
return B_ERROR;
}
static status_t
wb840_close(void* cookie)
{
wb_device* device = (wb_device*)cookie;
LOG((DEVICE_NAME ": close()\n"));
cancel_timer(&device->timer);
wb_stop(device);
write32(device->reg_base + WB_TXADDR, 0x00000000);
write32(device->reg_base + WB_RXADDR, 0x00000000);
wb_disable_interrupts(device);
remove_io_interrupt_handler(device->irq, wb_interrupt, device);
delete_sem(device->rxSem);
delete_sem(device->txSem);
return B_OK;
}
static status_t
wb840_free(void* cookie)
{
wb_device* device = (wb_device*)cookie;
LOG((DEVICE_NAME ": free()\n"));
sOpenMask &= ~(1L << device->devId);
wb_delete_rings(device);
free(device->firstPHY);
free(device);
return B_OK;
}
device_hooks
gDeviceHooks = {
wb840_open, /* -> open entry point */
wb840_close, /* -> close entry point */
wb840_free, /* -> free cookie */
wb840_control, /* -> control entry point */
wb840_read, /* -> read entry point */
wb840_write, /* -> write entry point */
NULL,
NULL,
NULL,
NULL
};
↑ V627 Consider inspecting the expression. The argument of sizeof() is the macro which expands to a number.