Driver for USB-to-Ethernet Adapters by ASIX.

It supports AX88172/AX88772/AX88178 models and clones.  


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28746 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
imker
2008-11-28 21:22:26 +00:00
parent d35d0f74a6
commit 69988fd576
16 changed files with 3068 additions and 0 deletions
@@ -0,0 +1,675 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include "Driver.h"
#include "Settings.h"
#include "ASIXDevice.h"
//TODO: multicast support
//TODO: set media state support
// frame header used during transfer data
struct TRXHeader {
uint16 fLength;
uint16 fInvertedLength;
TRXHeader(uint16 length = 0){ SetLength(length); }
bool IsValid() { return (fLength ^ fInvertedLength) == 0xffff; }
uint16 Length() { return fLength; }
//TODO: low-endian convertion?
void SetLength(uint16 length) {
fLength = length;
fInvertedLength = ~fLength;
}
};
ASIXDevice::ASIXDevice(usb_device device, const char *description)
: fStatus(B_ERROR),
fOpen(false),
fRemoved(false),
fInsideNotify(0),
fDevice(device),
fDescription(description),
fNonBlocking(false),
fFrameSize(0),
fNotifyEndpoint(0),
fReadEndpoint(0),
fWriteEndpoint(0),
fNotifyReadSem(-1),
fNotifyWriteSem(-1),
fNotifyBuffer(NULL),
fNotifyBufferLength(0),
fLinkStateChangeSem(-1),
fHasConnection(false),
fUseTRXHeader(false),
fReadNodeIDRequest(kInvalidRequest),
fReadRXControlRequest(kInvalidRequest),
fWriteRXControlRequest(kInvalidRequest),
fPromiscuousBits(0)
{
const usb_device_descriptor
*deviceDescriptor = gUSBModule->get_device_descriptor(device);
if (deviceDescriptor == NULL) {
TRACE_ALWAYS("Error of getting USB device descriptor.\n");
return;
}
fIPG[0] = 0x15;
fIPG[1] = 0x0c;
fIPG[2] = 0x12;
fVendorID = deviceDescriptor->vendor_id;
fProductID = deviceDescriptor->product_id;
fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
if (fNotifyReadSem < B_OK) {
TRACE_ALWAYS("Error of creating read notify semaphore:%#010x\n",
fNotifyReadSem);
return;
}
fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
if (fNotifyWriteSem < B_OK) {
TRACE_ALWAYS("Error of creating write notify semaphore:%#010x\n",
fNotifyWriteSem);
return;
}
if (_SetupEndpoints() != B_OK) {
return;
}
// must be set in derived class constructor
// fStatus = B_OK;
}
ASIXDevice::~ASIXDevice()
{
if (fNotifyReadSem >= B_OK)
delete_sem(fNotifyReadSem);
if (fNotifyWriteSem >= B_OK)
delete_sem(fNotifyWriteSem);
if (!fRemoved) //???
gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
if(fNotifyBuffer)
free(fNotifyBuffer);
}
status_t
ASIXDevice::Open(uint32 flags)
{
if (fOpen)
return B_BUSY;
if (fRemoved)
return B_ERROR;
status_t result = StartDevice();
if (result != B_OK) {
return result;
}
// setup state notifications
result = gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer,
fNotifyBufferLength, _NotifyCallback, this);
if(result != B_OK) {
TRACE_ALWAYS("Error of requesting notify interrupt:%#010x\n", result);
return result;
}
fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK;
fOpen = true;
return result;
}
status_t
ASIXDevice::Close()
{
if (fRemoved) {
fOpen = false;
return B_OK;
}
// wait until possible notification handling finished...
while (atomic_add(&fInsideNotify, 0) != 0)
snooze(100);
gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
gUSBModule->cancel_queued_transfers(fReadEndpoint);
gUSBModule->cancel_queued_transfers(fWriteEndpoint);
fOpen = false;
return StopDevice();
}
status_t
ASIXDevice::Free()
{
return B_OK;
}
status_t
ASIXDevice::Read(uint8 *buffer, size_t *numBytes)
{
size_t numBytesToRead = *numBytes;
*numBytes = 0;
if (fRemoved) {
TRACE_ALWAYS("Error of receiving %d bytes from removed device.\n",
numBytesToRead);
return B_ERROR;
}
TRACE_FLOW("Request %d bytes.\n", numBytesToRead);
TRXHeader header;
iovec rxData[] = {
{ &header, sizeof(TRXHeader) },
{ buffer, numBytesToRead }
};
size_t startIndex = fUseTRXHeader ? 0 : 1 ;
size_t chunkCount = fUseTRXHeader ? 2 : 1 ;
status_t result = gUSBModule->queue_bulk_v(fReadEndpoint,
&rxData[startIndex], chunkCount, _ReadCallback, this);
if (result != B_OK) {
TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result);
return result;
}
uint32 flags = B_CAN_INTERRUPT | (fNonBlocking ? B_TIMEOUT : 0);
result = acquire_sem_etc(fNotifyReadSem, 1, flags, 0);
if (result < B_OK) {
TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result);
return result;
}
if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) {
TRACE_ALWAYS("Device status error:%#010x\n", fStatusRead);
result = gUSBModule->clear_feature(fReadEndpoint,
USB_FEATURE_ENDPOINT_HALT);
if (result != B_OK) {
TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", result);
return result;
}
}
if(fUseTRXHeader) {
if(fActualLengthRead < sizeof(TRXHeader)) {
TRACE_ALWAYS("Error: no place for TRXHeader:only %d of %d bytes.\n",
fActualLengthRead, sizeof(TRXHeader));
return B_ERROR; //TODO: ???
}
if(!header.IsValid()) {
TRACE_ALWAYS("Error:TRX Header is invalid: len:%#04x; ilen:%#04x\n",
header.fLength, header.fInvertedLength);
return B_ERROR; //TODO: ???
}
*numBytes = header.Length();
if(fActualLengthRead - sizeof(TRXHeader) > header.Length()) {
TRACE_ALWAYS("MISMATCH of the frame length: hdr %d; received:%d\n",
header.Length(), fActualLengthRead - sizeof(TRXHeader));
}
} else {
*numBytes = fActualLengthRead;
}
TRACE_FLOW("Read %d bytes.\n", *numBytes);
return B_OK;
}
status_t
ASIXDevice::Write(const uint8 *buffer, size_t *numBytes)
{
size_t numBytesToWrite = *numBytes;
*numBytes = 0;
if (fRemoved) {
TRACE_ALWAYS("Error of writing %d bytes to removed device.\n",
numBytesToWrite);
return B_ERROR;
}
TRACE_FLOW("Write %d bytes.\n", numBytesToWrite);
TRXHeader header(numBytesToWrite);
iovec txData[] = {
{ &header, sizeof(TRXHeader) },
{ (uint8*)buffer, numBytesToWrite }
};
size_t startIndex = fUseTRXHeader ? 0 : 1 ;
size_t chunkCount = fUseTRXHeader ? 2 : 1 ;
status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint,
&txData[startIndex], chunkCount, _WriteCallback, this);
if (result != B_OK) {
TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result);
return result;
}
result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0);
if (result < B_OK) {
TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result);
return result;
}
if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) {
TRACE_ALWAYS("Device status error:%#010x\n", fStatusWrite);
result = gUSBModule->clear_feature(fWriteEndpoint,
USB_FEATURE_ENDPOINT_HALT);
if (result != B_OK) {
TRACE_ALWAYS("Error during clearing of HALT state:%#010x\n", result);
return result;
}
}
if(fUseTRXHeader) {
*numBytes = fActualLengthWrite - sizeof(TRXHeader);
} else {
*numBytes = fActualLengthWrite;
}
TRACE_FLOW("Written %d bytes.\n", *numBytes);
return B_OK;
}
status_t
ASIXDevice::Control(uint32 op, void *buffer, size_t length)
{
switch (op) {
case ETHER_INIT:
return B_OK;
case ETHER_GETADDR:
memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
return B_OK;
case ETHER_GETFRAMESIZE:
*(uint32 *)buffer = fFrameSize;
return B_OK;
case ETHER_NONBLOCK:
TRACE("ETHER_NONBLOCK\n");
fNonBlocking = *((uint8*)buffer);
return B_OK;
case ETHER_SETPROMISC:
TRACE("ETHER_SETPROMISC\n");
return SetPromiscuousMode(*((uint8*)buffer));
case ETHER_ADDMULTI:
TRACE("ETHER_ADDMULTI\n");
return ModifyMulticastTable(true, *((uint8*)buffer));
case ETHER_REMMULTI:
TRACE("ETHER_REMMULTI\n");
return ModifyMulticastTable(false, *((uint8*)buffer));
#if HAIKU_TARGET_PLATFORM_HAIKU
case ETHER_SET_LINK_STATE_SEM:
fLinkStateChangeSem = *(sem_id *)buffer;
return B_OK;
case ETHER_GET_LINK_STATE:
return GetLinkState((ether_link_state *)buffer);
#endif
default:
TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op);
}
return B_DEV_INVALID_IOCTL;
}
void
ASIXDevice::Removed()
{
fRemoved = true;
fHasConnection = false;
// the notify hook is different from the read and write hooks as it does
// itself schedule traffic (while the other hooks only release a semaphore
// to notify another thread which in turn safly checks for the removed
// case) - so we must ensure that we are not inside the notify hook anymore
// before returning, as we would otherwise violate the promise not to use
// any of the pipes after returning from the removed hook
while (atomic_add(&fInsideNotify, 0) != 0)
snooze(100);
gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
gUSBModule->cancel_queued_transfers(fReadEndpoint);
gUSBModule->cancel_queued_transfers(fWriteEndpoint);
if (fLinkStateChangeSem >= B_OK)
release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
}
status_t
ASIXDevice::SetupDevice(bool deviceReplugged)
{
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]);
if(deviceReplugged) {
// this might be the same device that was replugged - read the MAC address
// (which should be at the same index) to make sure
if(memcmp(&address, &fMACAddress, sizeof(address)) != 0) {
TRACE_ALWAYS("Cannot replace device with MAC address:"
"%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_BAD_VALUE; // is not the same
}
} else
fMACAddress = address;
return B_OK;
}
status_t
ASIXDevice::CompareAndReattach(usb_device device)
{
const usb_device_descriptor *deviceDescriptor
= gUSBModule->get_device_descriptor(device);
if (deviceDescriptor == NULL) {
TRACE_ALWAYS("Error of getting USB device descriptor.\n");
return B_ERROR;
}
if (deviceDescriptor->vendor_id != fVendorID
&& deviceDescriptor->product_id != fProductID) {
// this certainly isn't the same device
return B_BAD_VALUE;
}
// this is the same device that was replugged - clear the removed state,
// re-setup the endpoints and transfers and open the device if it was
// previously opened
fDevice = device;
fRemoved = false;
status_t result = _SetupEndpoints();
if (result != B_OK) {
fRemoved = true;
return result;
}
// we need to setup hardware on device replug
result = SetupDevice(true);
if (result != B_OK) {
return result;
}
if (fOpen) {
fOpen = false;
result = Open(fNonBlocking ? O_NONBLOCK : 0);
}
return result;
}
status_t
ASIXDevice::_SetupEndpoints()
{
const usb_configuration_info *config
= gUSBModule->get_nth_configuration(fDevice, 0);
if (config == NULL) {
TRACE_ALWAYS("Error of getting USB device configuration.\n");
return B_ERROR;
}
if (config->interface_count <= 0) {
TRACE_ALWAYS("Error:no interfaces found in USB device configuration\n");
return B_ERROR;
}
usb_interface_info *interface = config->interface[0].active;
if (interface == 0) {
TRACE_ALWAYS("Error:invalid active interface in "
"USB device configuration\n");
return B_ERROR;
}
int notifyEndpoint = -1;
int readEndpoint = -1;
int writeEndpoint = -1;
for(size_t ep = 0; ep < interface->endpoint_count; ep++) {
usb_endpoint_descriptor *epd = interface->endpoint[ep].descr;
if((epd->attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_INTERRUPT) {
notifyEndpoint = ep;
continue;
}
if((epd->attributes & USB_ENDPOINT_ATTR_MASK) != USB_ENDPOINT_ATTR_BULK) {
TRACE_ALWAYS("Error: USB endpoint type %#04x is unknown.\n", epd->attributes);
continue;
}
if((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
== USB_ENDPOINT_ADDR_DIR_IN) {
readEndpoint = ep;
continue;
}
if((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_OUT)
== USB_ENDPOINT_ADDR_DIR_OUT) {
writeEndpoint = ep;
continue;
}
}
if (notifyEndpoint == -1 || readEndpoint == -1 || writeEndpoint == -1) {
TRACE_ALWAYS("Error: not all USB endpoints were found: "
"notify:%d; read:%d; write:%d\n",
notifyEndpoint, readEndpoint, writeEndpoint);
return B_ERROR;
}
gUSBModule->set_configuration(fDevice, config);
fNotifyEndpoint = interface->endpoint[notifyEndpoint].handle;
fReadEndpoint = interface->endpoint[readEndpoint ].handle;
fWriteEndpoint = interface->endpoint[writeEndpoint ].handle;
return B_OK;
}
status_t
ASIXDevice::ReadMACAddress(ether_address_t *address)
{
size_t actual_length = 0;
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
fReadNodeIDRequest, 0, 0, sizeof(ether_address),
address, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result);
return result;
}
if(actual_length != sizeof(ether_address)) {
TRACE_ALWAYS("Mismatch of NODE ID data size: %d instead of %d bytes\n",
actual_length, sizeof(ether_address));
return B_ERROR;
}
return B_OK;
}
status_t
ASIXDevice::ReadRXControlRegister(uint16 *rxcontrol)
{
size_t actual_length = 0;
*rxcontrol = 0;
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
fReadRXControlRequest, 0, 0,
sizeof(*rxcontrol), rxcontrol, &actual_length);
if(sizeof(*rxcontrol) != actual_length) {
TRACE_ALWAYS("Mismatch during reading RX control register."
"Read %d bytes instead of %d.\n",
actual_length, sizeof(*rxcontrol));
}
return result;
}
status_t
ASIXDevice::WriteRXControlRegister(uint16 rxcontrol)
{
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fWriteRXControlRequest, rxcontrol, 0, 0, 0, 0);
return result;
}
status_t
ASIXDevice::StopDevice()
{
status_t result = WriteRXControlRegister(0);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result);
}
TRACE_RET(result);
return result;
}
status_t
ASIXDevice::SetPromiscuousMode(bool on)
{
uint16 rxcontrol = 0;
status_t result = ReadRXControlRegister(&rxcontrol);
if(result != B_OK) {
TRACE_ALWAYS("Error of reading RX Control:%#010x\n", result);
return result;
}
if(on)
rxcontrol |= fPromiscuousBits;
else
rxcontrol &= ~fPromiscuousBits;
result = WriteRXControlRegister(rxcontrol);
if(result != B_OK ) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", rxcontrol, result);
}
TRACE_RET(result);
return result;
}
status_t
ASIXDevice::ModifyMulticastTable(bool add, uint8 address)
{
//TODO: !!!
TRACE_ALWAYS("Call for (%d, %#02x) is not implemented\n", add, address);
return B_OK;
}
void
ASIXDevice::_ReadCallback(void *cookie, int32 status, void *data,
uint32 actualLength)
{
TRACE_FLOW("ReadCB: %d bytes; status:%#010x\n", actualLength, status);
ASIXDevice *device = (ASIXDevice *)cookie;
device->fActualLengthRead = actualLength;
device->fStatusRead = status;
release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
}
void
ASIXDevice::_WriteCallback(void *cookie, int32 status, void *data,
uint32 actualLength)
{
TRACE_FLOW("WriteCB: %d bytes; status:%#010x\n", actualLength, status);
ASIXDevice *device = (ASIXDevice *)cookie;
device->fActualLengthWrite = actualLength;
device->fStatusWrite = status;
release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
}
void
ASIXDevice::_NotifyCallback(void *cookie, int32 status, void *data,
uint32 actualLength)
{
ASIXDevice *device = (ASIXDevice *)cookie;
atomic_add(&device->fInsideNotify, 1);
if (status == B_CANCELED || device->fRemoved) {
atomic_add(&device->fInsideNotify, -1);
return;
}
if (status != B_OK) {
TRACE_ALWAYS("Device status error:%#010x\n", status);
status_t result = gUSBModule->clear_feature(device->fNotifyEndpoint,
USB_FEATURE_ENDPOINT_HALT);
if(result != B_OK)
TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", result);
}
// parse data in overriden class
device->OnNotify(actualLength);
// schedule next notification buffer
gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer,
device->fNotifyBufferLength, _NotifyCallback, device);
atomic_add(&device->fInsideNotify, -1);
}
@@ -0,0 +1,111 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_ASIX_DEVICE_H_
#define _USB_ASIX_DEVICE_H_
#include <net/if_media.h>
#include "Driver.h"
#include "MIIBus.h"
class ASIXDevice {
public:
ASIXDevice(usb_device device, const char *description);
virtual ~ASIXDevice();
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);
void Removed();
bool IsRemoved() { return fRemoved; };
status_t CompareAndReattach(usb_device device);
virtual status_t SetupDevice(bool deviceReplugged);
private:
static void _ReadCallback(void *cookie, int32 status,
void *data, uint32 actualLength);
static void _WriteCallback(void *cookie, int32 status,
void *data, uint32 actualLength);
static void _NotifyCallback(void *cookie, int32 status,
void *data, uint32 actualLength);
status_t _SetupEndpoints();
protected:
/* overrides */
virtual status_t StartDevice() = 0;
virtual status_t StopDevice();
virtual status_t OnNotify(uint32 actualLength) = 0;
virtual status_t GetLinkState(ether_link_state *state) = 0;
virtual status_t SetPromiscuousMode(bool bOn);
virtual status_t ModifyMulticastTable(bool add, uint8 address);
status_t ReadMACAddress(ether_address_t *address);
status_t ReadRXControlRegister(uint16 *rxcontrol);
status_t WriteRXControlRegister(uint16 rxcontrol);
// state tracking
status_t fStatus;
bool fOpen;
bool fRemoved;
vint32 fInsideNotify;
usb_device fDevice;
uint16 fVendorID;
uint16 fProductID;
const char * fDescription;
bool fNonBlocking;
// interface and device infos
uint16 fFrameSize;
// pipes for notifications and data io
usb_pipe fNotifyEndpoint;
usb_pipe fReadEndpoint;
usb_pipe fWriteEndpoint;
// data stores for async usb transfers
uint32 fActualLengthRead;
uint32 fActualLengthWrite;
int32 fStatusRead;
int32 fStatusWrite;
sem_id fNotifyReadSem;
sem_id fNotifyWriteSem;
uint8 * fNotifyBuffer;
uint32 fNotifyBufferLength;
// MII bus handler
MIIBus fMII;
// connection data
sem_id fLinkStateChangeSem;
ether_address_t fMACAddress;
bool fHasConnection;
bool fUseTRXHeader;
uint8 fIPG[3];
uint8 fReadNodeIDRequest;
uint8 fReadRXControlRequest;
uint8 fWriteRXControlRequest;
uint16 fPromiscuousBits;
};
#endif //_USB_ASIX_DEVICE_H_
@@ -0,0 +1,274 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include "Settings.h"
#include "AX88172Device.h"
enum AX88172_Requests {
READ_RXTX_SRAM = 0x02, // C0 02 XX YY 0M 00 0200 Read Rx/Tx SRAM
// M = 0 : Rx, M=1 : Tx
WRITE_RX_SRAM = 0x03, // 40 03 XX YY PP QQ 0000 Write Rx SRAM
WRITE_TX_SRAM = 0x04, // 40 04 XX YY PP QQ 0000 Write Tx SRAM
SW_MII_OP = 0x06, // 40 06 00 00 00 00 0000 Software MII Operation
READ_MII = 0x07, // C0 07 PI 00 RG 00 0200 Read MII Register
WRITE_MII = 0x08, // 40 08 PI 00 RG 00 0200 Write MII Register
READ_MII_OP_MODE = 0x09, // C0 09 00 00 00 00 0100 Read MII Operation Mode
HW_MII_OP = 0x0A, // 40 0A 00 00 00 00 0000 Hardware MII Operation
READ_SROM = 0x0B, // C0 0B DR 00 00 00 0200 Read SROM
WRITE_SROM = 0x0C, // 40 0C DR 00 MM SS 0000 Write SROM
WRITE_SROM_ENABLE = 0x0D, // 40 0D 00 00 00 00 0000 Write SROM Enable
WRITE_SROM_DISABLE = 0x0E, // 40 0E 00 00 00 00 0000 Write SROM Disable
READ_RX_CONTROL = 0x0F, // C0 0F 00 00 00 00 0200 Read Rx Control Register
WRITE_RX_CONTROL = 0x10, // 40 10 RR 00 00 00 0000 Write Rx Control Register
READ_IPGS = 0x11, // C0 11 00 00 00 00 0300 Read IPG/IPG1/IPG2 Register
WRITE_IPG0 = 0x12, // 40 12 II 00 00 00 0000 Write IPG Register
WRITE_IPG1 = 0x13, // 40 13 II 00 00 00 0000 Write IPG1 Register
WRITE_IPG2 = 0x14, // 40 14 II 00 00 00 0000 Write IPG2 Register
READ_MF_ARRAY = 0x15, // C0 15 00 00 00 00 0800 Read Multi-Filter Array
WRITE_MF_ARRAY = 0x16, // 40 16 00 00 00 00 0800 Write Multi-Filter Array
READ_NODEID = 0x17, // C0 17 00 00 00 00 0600 Read Node ID
WRITE_NODEID = 0x18, //
READ_PHYID = 0x19, // C0 19 00 00 00 00 0200 Read Ethernet/HomePNA PhyID
READ_MEDIUM_STATUS = 0x1A, // C0 1A 00 00 00 00 0100 Read Medium Status
WRITE_MEDIUM_MODE = 0x1B, // 40 1B MM 00 00 00 0000 Write Medium Mode
GET_MONITOR_MODE = 0x1C, // C0 1C 00 00 00 00 0100 Get Monitor Mode Status
SET_MONITOR_MODE = 0x1D, // 40 1D MM 00 00 00 0000 Set Monitor Mode On/Off
READ_GPIOS = 0x1E, // C0 1E 00 00 00 00 0100 Read GPIOs
WRITE_GPIOS = 0x1F // 40 1F MM 00 00 00 0000 Write GPIOs
};
// RX Control Register bits
enum AX88172_RXControl {
RXCTL_PROMISCUOUS = 0x0001, //
RXCTL_ALL_MULTICAT = 0x0002, //
RXCTL_UNICAST = 0x0004, // ???
RXCTL_BROADCAST = 0x0008, //
RXCTL_MULTICAST = 0x0010, //
RXCTL_START = 0x0080 //
};
// PHY IDs request answer data layout
struct PhyIDs {
uint8 PhyID1;
uint8 PhyID2;
} _PACKED;
// Medium state bits
enum AX88172_MediumState {
MEDIUM_STATE_FULL_DUPLEX = 0x02,
MEDIUM_STATE_TX_ABORT_ALLOW = 0x04,
MEDIUM_STATE_FLOW_CONTOL_EN = 0x10
};
// Monitor Mode bits
enum AX88172_MonitorMode {
MONITOR_MODE = 0x01,
MONITOR_MODE_LINK_UP_WAKE = 0x02,
MONITOR_MODE_MAGIC_PACKET_EN = 0x04,
MONITOR_MODE_HS_FS = 0x10
};
// General Purpose I/O Register
enum AX88172_GPIO {
GPIO_OO_0EN = 0x01,
GPIO_IO_0 = 0x02,
GPIO_OO_1EN = 0x04,
GPIO_IO_1 = 0x08,
GPIO_OO_2EN = 0x10,
GPIO_IO_2 = 0x20
};
// Notification data layout
struct AX88172Notify {
uint8 btA1;
uint8 bt01;
uint8 btNN; // AX88172_LinkState below
uint8 bt03;
uint8 bt04;
uint8 bt80; // 90h
uint8 bt06;
uint8 bt07;
} _PACKED;
// Link-State bits
enum AX88172_LinkState {
LINK_STATE_PHY1 = 0x01,
LINK_STATE_PHY2 = 0x02
};
const uint16 maxFrameSize = 1518;
AX88172Device::AX88172Device(usb_device device, const char *description)
: ASIXDevice(device, description)
{
fStatus = InitDevice();
}
status_t
AX88172Device::InitDevice()
{
fFrameSize = maxFrameSize;
fReadNodeIDRequest = READ_NODEID;
fReadRXControlRequest = READ_RX_CONTROL;
fWriteRXControlRequest = WRITE_RX_CONTROL;
fPromiscuousBits = RXCTL_PROMISCUOUS;
fNotifyBufferLength = sizeof(AX88172Notify);
fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
if (fNotifyBuffer == NULL) {
TRACE_ALWAYS("Error of allocating memory for notify buffer.\n");
return B_NO_MEMORY;
}
TRACE_RET(B_OK);
return B_OK;
}
status_t
AX88172Device::SetupDevice(bool deviceReplugged)
{
status_t result = ASIXDevice::SetupDevice(deviceReplugged);
if(result != B_OK) {
return result;
}
result = fMII.Init(fDevice,
SW_MII_OP, READ_MII, WRITE_MII,
READ_MII_OP_MODE, HW_MII_OP, READ_PHYID);
if(result == B_OK)
return fMII.SetupPHY();
TRACE_RET(result);
return result;
}
status_t
AX88172Device::StartDevice()
{
size_t actualLength = 0;
for(size_t i = 0; i < sizeof(fIPG)/sizeof(fIPG[0]); i++) {
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_IPG0, 0, 0, sizeof(fIPG[i]), &fIPG[i], &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error writing IPG%d: %#010x\n", i, result);
return result;
}
if(actualLength != sizeof(fIPG[i])) {
TRACE_ALWAYS("Mismatch of written IPG%d data. "
"%d bytes of %d written.\n", i, actualLength, sizeof(fIPG[i]));
}
}
uint16 rxcontrol = RXCTL_START | RXCTL_MULTICAST | RXCTL_UNICAST;
status_t result = WriteRXControlRegister(rxcontrol);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", rxcontrol, result);
}
TRACE_RET(result);
return result;
}
status_t
AX88172Device::OnNotify(uint32 actualLength)
{
if (actualLength < sizeof(AX88172Notify)) {
TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n",
actualLength, sizeof(AX88172Notify));
return B_BAD_DATA;
}
AX88172Notify *notification = (AX88172Notify *)fNotifyBuffer;
if(notification->btA1 != 0xa1) {
TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n",
notification->btA1);
}
uint phyIndex = 0;
bool linkIsUp = fHasConnection;
switch(fMII.ActivePHY()) {
case PrimaryPHY:
phyIndex = 1;
linkIsUp = (notification->btNN & LINK_STATE_PHY1) == LINK_STATE_PHY1;
break;
case SecondaryPHY:
phyIndex = 2;
linkIsUp = (notification->btNN & LINK_STATE_PHY2) == LINK_STATE_PHY2;
break;
default:
case CurrentPHY:
TRACE_ALWAYS("Error: PHY is not initialized.\n");
return B_NO_INIT;
}
bool linkStateChange = linkIsUp != fHasConnection;
fHasConnection = linkIsUp;
if(linkStateChange) {
TRACE("Link state of PHY%d has been changed to '%s'\n",
phyIndex, fHasConnection ? "up" : "down");
}
if (linkStateChange && fLinkStateChangeSem >= B_OK)
release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
return B_OK;
}
status_t
AX88172Device::GetLinkState(ether_link_state *linkState)
{
uint16 miiANAR = 0;
uint16 miiANLPAR = 0;
status_t result = fMII.Read(MII_ANAR, &miiANAR);
if(result != B_OK) {
TRACE_ALWAYS("Error reading MII ANAR register:%#010x\n", result);
return result;
}
result = fMII.Read(MII_ANLPAR, &miiANLPAR);
if(result != B_OK) {
TRACE_ALWAYS("Error reading MII ANLPAR register:%#010x\n", result);
return result;
}
TRACE_FLOW("ANAR:%04x ANLPAR:%04x\n", miiANAR, miiANLPAR);
uint16 mediumStatus = miiANAR & miiANLPAR;
linkState->quality = 1000;
linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0);
linkState->media |= mediumStatus & (ANLPAR_TX_FD | ANLPAR_10_FD) ?
IFM_FULL_DUPLEX : IFM_HALF_DUPLEX;
linkState->speed = mediumStatus & (ANLPAR_TX_FD | ANLPAR_TX_HD) ? 100000 : 10000;
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 B_OK;
}
@@ -0,0 +1,30 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_AX88172_DEVICE_H_
#define _USB_AX88172_DEVICE_H_
#include "ASIXDevice.h"
class AX88172Device : public ASIXDevice {
public:
AX88172Device(usb_device device, const char *description);
protected:
status_t InitDevice();
virtual status_t SetupDevice(bool deviceReplugged);
virtual status_t StartDevice();
virtual status_t OnNotify(uint32 actualLength);
virtual status_t GetLinkState(ether_link_state *state);
};
#endif //_USB_AX88172_DEVICE_H_
@@ -0,0 +1,418 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#include "Settings.h"
#include "AX88178Device.h"
// Vendor USB requests for AX88178
enum AX88178_Requests {
READ_RXTX_SRAM = 0x02, //C002_AA0B_0C00_0800 Rx/Tx SRAM Read
WRITE_RXTX_SRAM = 0x03, //4003_AA0B_0C00_0800 Rx/Tx SRAM Write
SW_MII_OP = 0x06, //4006_0000_0000_0000 SW Serial Management Control
READ_MII = 0x07, //c007_aa00_cc00_0200 PHY Read
WRITE_MII = 0x08, //4008_aa00_cc00_0200 PHY Write
READ_MII_STATUS = 0x09, //c009_0000_0000_0100 Serial Management Status
HW_MII_OP = 0x0A, //400a_0000_0000_0000 HW Serial Management Control
READ_SROM = 0x0B, //C00B_AA00_0000_0200 SROM Read
WRITE_SROM = 0x0C, //400C_AA00_CCDD_0000 SROM Write
WRITE_SROM_ENABLE = 0x0D, //400D_0000_0000_0000 SROM Write Enable
WRITE_SROM_DISABLE = 0x0E, //400E_0000_0000_0000 SROM Write Disable
READ_RX_CONTROL = 0x0F, //C00F_0000_0000_0200 Read Rx Control
WRITE_RX_CONTROL = 0x10, //4010_AABB_0000_0000 Write Rx Control
READ_IPGS = 0x11, //C011_0000_0000_0300 Read IPG/IPG1/IPG2 Register
WRITE_IPGS = 0x12, //4012_AABB_CC00_0000 Write IPG/IPG1/IPG2 Register
READ_NODEID = 0x13, //C013_0000_0000_0600 Read Node ID
WRITE_NODEID = 0x14, //4014_0000_0000_0600 Write Node ID
READ_MF_ARRAY = 0x15, //C015_0000_0000_0800 Read Multicast Filter Array
WRITE_MF_ARRAY = 0x16, //4016_0000_0000_0800 Write Multicast Filter Array
READ_TEST = 0x17, //4017_AA00_0000_0000 Write Test Register
READ_PHYID = 0x19, //C019_0000_0000_0200 Read Ethernet/HomePNA PHY Address
READ_MEDIUM_STATUS = 0x1A, //C01A_0000_0000_0200 Read Medium Status
WRITE_MEDIUM_MODE = 0x1B, //401B_AABB_0000_0000 Write Medium Mode Register
GET_MONITOR_MODE = 0x1C, //C01C_0000_0000_0100 Read Monitor Mode Status
SET_MONITOR_MODE = 0x1D, //401D_AA00_0000_0000 Write Monitor Mode Register
READ_GPIOS = 0x1E, //C01E_0000_0000_0100 Read GPIOs Status
WRITE_GPIOS = 0x1F, //401F_AA00_0000_0000 Write GPIOs
WRITE_SOFT_RESET = 0x20, //4020_AA00_0000_0000 Write Software Reset
READ_MIIS_IF_STATE = 0x21, //C021_AA00_0000_0100 Read MII/GMII/RGMII Interface Status
WRITE_MIIS_IF_STATE = 0x22 //4022_AA00_0000_0000 Write MII/GMII/RGMII Interface Control
};
// RX Control Register bits
enum AX88178_RXControl {
RXCTL_PROMISCUOUS = 0x0001, //
RXCTL_ALL_MULTICAT = 0x0002, //
// RXCTL_SEP = 0x0004, // do not set it!
RXCTL_BROADCAST = 0x0008, //
RXCTL_MULTICAST = 0x0010, //
RXCTL_AP = 0x0020, //
RXCTL_START = 0x0080, //
RXCTL_USB_MFB = 0x0100 // Max Frame Burst TX on USB
};
// PHY IDs request answer data layout
struct AX88178_PhyIDs {
uint8 SecPhyID;
uint8 PriPhyID2;
} _PACKED;
// Medium state bits
enum AX88178_MediumState {
MEDIUM_STATE_GM = 0x0001,
MEDIUM_STATE_FD = 0x0002,
MEDIUM_STATE_AC = 0x0004, // must be always set
MEDIUM_STATE_ENCK = 0x0008,
MEDIUM_STATE_RFC = 0x0010,
MEDIUM_STATE_TFC = 0x0020,
MEDIUM_STATE_JFE = 0x0040,
MEDIUM_STATE_PF_ON = 0x0080,
MEDIUM_STATE_PF_OFF = 0x0000,
MEDIUM_STATE_RE = 0x0100,
MEDIUM_STATE_PS_100 = 0x0200,
MEDIUM_STATE_PS_10 = 0x0000,
MEDIUM_STATE_SBP1 = 0x0800,
MEDIUM_STATE_SBP0 = 0x0000,
MEDIUM_STATE_SM_ON = 0x1000
};
// Monitor Mode bits
enum AX88178_MonitorMode {
MONITOR_MODE_MOM = 0x01,
MONITOR_MODE_RWLU = 0x02,
MONITOR_MODE_RWMP = 0x04,
MONITOR_MODE_US = 0x10
};
// General Purpose I/O Register
enum AX88178_GPIO {
GPIO_OO_0EN = 0x01,
GPIO_IO_0 = 0x02,
GPIO_OO_1EN = 0x04,
GPIO_IO_1 = 0x08,
GPIO_OO_2EN = 0x10,
GPIO_IO_2 = 0x20,
GPIO_RSE = 0x80
};
// Software Reset Register bits
enum AX88178_SoftwareReset {
SW_RESET_RR = 0x01,
SW_RESET_RT = 0x02,
SW_RESET_PRTE = 0x04,
SW_RESET_PRL = 0x08,
SW_RESET_BZ = 0x10,
SW_RESET_BIT6 = 0x40 // always set to 1
};
// MII/GMII/RGMII Interface Conttrol
enum AX88178_MIISInterfaceStatus {
MIIS_IF_STATE_DM = 0x01,
MIIS_IF_STATE_RB = 0x02
};
// Notification data layout
struct AX88178_Notify {
uint8 btA1;
uint8 bt01;
uint8 btBB; // AX88178_BBState below
uint8 bt03;
uint16 regCCDD;
uint16 regEEFF;
} _PACKED;
// Link-State bits
enum AX88178_BBState {
LINK_STATE_PPLS = 0x01,
LINK_STATE_SPLS = 0x02,
LINK_STATE_FLE = 0x04,
LINK_STATE_MDINT = 0x08
};
const uint16 maxFrameSize = 1536;
AX88178Device::AX88178Device(usb_device device, const char *description)
: ASIXDevice(device, description)
{
fStatus = InitDevice();
}
status_t
AX88178Device::InitDevice()
{
fFrameSize = maxFrameSize;
fUseTRXHeader = true;
fReadNodeIDRequest = READ_NODEID;
fReadRXControlRequest = READ_RX_CONTROL;
fWriteRXControlRequest = WRITE_RX_CONTROL;
fPromiscuousBits = RXCTL_PROMISCUOUS;
fNotifyBufferLength = sizeof(AX88178_Notify);
fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
if (fNotifyBuffer == NULL) {
TRACE_ALWAYS("Error of allocating memory for notify buffer.\n");
return B_NO_MEMORY;
}
TRACE_RET(B_OK);
return B_OK;
}
status_t
AX88178Device::SetupDevice(bool deviceReplugged)
{
status_t result = ASIXDevice::SetupDevice(deviceReplugged);
if(result != B_OK) {
return result;
}
result = fMII.Init(fDevice,
SW_MII_OP, READ_MII, WRITE_MII,
READ_MII_STATUS, HW_MII_OP, READ_PHYID);
if(result != B_OK) {
return result;
}
size_t actualLength = 0;
// get the "magic" word from EEPROM
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_SROM_ENABLE, 0, 0, 0, 0, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of enabling SROM access:%#010x\n", result);
return result;
}
uint16 eepromData = 0;
status_t op_result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
READ_SROM, 0x17, 0,
sizeof(eepromData), &eepromData, &actualLength);
if(op_result != B_OK) {
TRACE_ALWAYS("Error of reading SROM data:%#010x\n", result);
}
if(actualLength != sizeof(eepromData)) {
TRACE_ALWAYS("Mismatch of reading SROM data."
"Read %d bytes instead of %d\n",
actualLength, sizeof(eepromData));
}
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_SROM_DISABLE, 0, 0, 0, 0, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of disabling SROM access: %#010x\n", result);
return result;
}
if(op_result != B_OK) {
return op_result;
}
// some shaman's dances with GPIO
struct GPIOData {
bigtime_t delay;
uint16 value;
} GPIOCommands[] = {
// eeprom bit 8 is off
{ 40000 , GPIO_OO_1EN | GPIO_IO_1 | GPIO_RSE },
{ 30000 , GPIO_OO_2EN | GPIO_IO_2 | GPIO_OO_1EN | GPIO_IO_1 },
{ 300000 , GPIO_OO_2EN | GPIO_OO_1EN | GPIO_IO_1 },
{ 30000 , GPIO_OO_2EN | GPIO_IO_2 | GPIO_OO_1EN | GPIO_IO_1 },
// eeprom bit 8 is on
{ 40000 , GPIO_OO_1EN | GPIO_IO_1 | GPIO_RSE },
{ 30000 , GPIO_OO_1EN },
{ 30000 , GPIO_OO_1EN | GPIO_IO_1 },
};
bool bCase8 = (eepromData >> 8) != 1;
size_t from = bCase8 ? 0 : 4;
size_t to = bCase8 ? 3 : 6;
for(size_t i = from; i <= to; i++) {
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_GPIOS, GPIOCommands[i].value,
0, 0, 0, &actualLength);
snooze(GPIOCommands[i].delay);
if(result != B_OK) {
TRACE_ALWAYS("Error of GPIO setup command %d:[%#04x]: %#010x\n",
i, GPIOCommands[i].value, result);
return result;
}
}
uint8 uSWReset = 0;
// finally a bit of exercises for SW reset register...
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_SOFT_RESET, uSWReset, 0, 0, 0, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of SW reset to %#02x: %#010x\n", uSWReset, result);
return result;
}
snooze(150000);
uSWReset = SW_RESET_PRL | SW_RESET_BIT6;
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_SOFT_RESET, uSWReset, 0, 0, 0, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of SW reset to %#02x: %#010x\n", uSWReset, result);
return result;
}
snooze(150000);
result = WriteRXControlRegister(0);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result);
return result;
}
result = fMII.SetupPHY();
TRACE_RET(result);
return result;
}
status_t
AX88178Device::StartDevice()
{
size_t actualLength = 0;
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_IPGS, 0, 0, sizeof(fIPG), fIPG, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing IPGs:%#010x\n", result);
return result;
}
if(actualLength != sizeof(fIPG)) {
TRACE_ALWAYS("Mismatch of written IPGs data. "
"%d bytes of %d written.\n", actualLength, sizeof(fIPG));
}
uint16 rxcontrol = RXCTL_START | RXCTL_MULTICAST;
result = WriteRXControlRegister(rxcontrol);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", rxcontrol, result);
}
TRACE_RET(result);
return result;
}
status_t
AX88178Device::OnNotify(uint32 actualLength)
{
if (actualLength < sizeof(AX88178_Notify)) {
TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n",
actualLength, sizeof(AX88178_Notify));
return B_BAD_DATA;
}
AX88178_Notify *notification = (AX88178_Notify *)fNotifyBuffer;
if(notification->btA1 != 0xa1) {
TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n",
notification->btA1);
}
uint phyIndex = 0;
bool linkIsUp = fHasConnection;
switch(fMII.ActivePHY()) {
case PrimaryPHY:
phyIndex = 1;
linkIsUp = (notification->btBB & LINK_STATE_PPLS) == LINK_STATE_PPLS;
break;
case SecondaryPHY:
phyIndex = 2;
linkIsUp = (notification->btBB & LINK_STATE_SPLS) == LINK_STATE_SPLS;
break;
default:
case CurrentPHY:
TRACE_ALWAYS("Error: PHY is not initialized.\n");
return B_NO_INIT;
}
bool linkStateChange = linkIsUp != fHasConnection;
fHasConnection = linkIsUp;
if(linkStateChange) {
TRACE("Link state of PHY%d has been changed to '%s'\n",
phyIndex, fHasConnection ? "up" : "down");
}
if (linkStateChange && fLinkStateChangeSem >= B_OK)
release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
return B_OK;
}
status_t
AX88178Device::GetLinkState(ether_link_state *linkState)
{
size_t actualLength = 0;
uint16 mediumStatus = 0;
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
READ_MEDIUM_STATUS, 0, 0, sizeof(mediumStatus),
&mediumStatus, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of reading medium status:%#010x.\n", result);
return result;
}
if(actualLength != sizeof(mediumStatus)) {
TRACE_ALWAYS("Mismatch of reading medium status."
"Read %d bytes instead of %d\n",
actualLength, sizeof(mediumStatus));
}
TRACE_FLOW("Medium status is %#04x\n", mediumStatus);
linkState->quality = 1000;
linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0);
linkState->media |= (mediumStatus & MEDIUM_STATE_FD) ?
IFM_FULL_DUPLEX : IFM_HALF_DUPLEX;
linkState->speed = (mediumStatus & MEDIUM_STATE_PS_100) ? 100000 : 10000;
linkState->speed = (mediumStatus & MEDIUM_STATE_GM) ?
1000000 : linkState->speed;
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 B_OK;
}
@@ -0,0 +1,29 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_AX88178_DEVICE_H_
#define _USB_AX88178_DEVICE_H_
#include "ASIXDevice.h"
class AX88178Device : public ASIXDevice {
public:
AX88178Device(usb_device device, const char *description);
protected:
status_t InitDevice();
virtual status_t SetupDevice(bool deviceReplugged);
virtual status_t StartDevice();
virtual status_t OnNotify(uint32 actualLength);
virtual status_t GetLinkState(ether_link_state *state);
};
#endif //_USB_AX88178_DEVICE_H_
@@ -0,0 +1,393 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#include "Settings.h"
#include "AX88772Device.h"
enum AX88772_Requests {
READ_RXTX_SRAM = 0x02, //C002_AA0B_0C00_0800 Rx/Tx SRAM Read
WRITE_RXTX_SRAM = 0x03, //4003_AA0B_0C00_0800 Rx/Tx SRAM Write
SW_MII_OP = 0x06, //4006_0000_0000_0000 SW Serial Management Control
READ_MII = 0x07, //c007_aa00_cc00_0200 PHY Read
WRITE_MII = 0x08, //4008_aa00_cc00_0200 PHY Write
READ_MII_OP_MODE = 0x09, //c009_0000_0000_0100 Serial Management Status
HW_MII_OP = 0x0A, //400a_0000_0000_0000 HW Serial Management Control
READ_SROM = 0x0B, //C00B_AA00_0000_0200 SROM Read
WRITE_SROM = 0x0C, //400C_AA00_CCDD_0000 SROM Write
WRITE_SROM_ENABLE = 0x0D, //400D_0000_0000_0000 SROM Write Enable
WRITE_SROM_DISABLE = 0x0E, //400E_0000_0000_0000 SROM Write Disable
READ_RX_CONTROL = 0x0F, //C00F_0000_0000_0200 Read Rx Control
WRITE_RX_CONTROL = 0x10, //4010_AABB_0000_0000 Write Rx Control
READ_IPGS = 0x11, //C011_0000_0000_0300 Read IPG/IPG1/IPG2 Register
WRITE_IPGS = 0x12, //4012_AABB_CC00_0000 Write IPG/IPG1/IPG2 Register
READ_NODEID = 0x13, //C013_0000_0000_0600 Read Node ID
WRITE_NODEID = 0x14, //4014_0000_0000_0600 Write Node ID
READ_MF_ARRAY = 0x15, //C015_0000_0000_0800 Read Multicast Filter Array
WRITE_MF_ARRAY = 0x16, //4016_0000_0000_0800 Write Multicast Filter Array
READ_TEST = 0x17, //4017_AA00_0000_0000 Write Test Register
READ_PHYID = 0x19, //C019_0000_0000_0200 Read Ethernet/HomePNA PHY Address
READ_MEDIUM_STATUS = 0x1A, //C01A_0000_0000_0200 Read Medium Status
WRITE_MEDIUM_MODE = 0x1B, //401B_AABB_0000_0000 Write Medium Mode Register
GET_MONITOR_MODE = 0x1C, //C01C_0000_0000_0100 Read Monitor Mode Status
SET_MONITOR_MODE = 0x1D, //401D_AA00_0000_0000 Write Monitor Mode Register
READ_GPIOS = 0x1E, //C01E_0000_0000_0100 Read GPIOs Status
WRITE_GPIOS = 0x1F, //401F_AA00_0000_0000 Write GPIOs
WRITE_SOFT_RESET = 0x20, //4020_AA00_0000_0000 Write Software Reset
READ_PHY_SEL_STATE = 0x21, //C021_AA00_0000_0100 Read Software PHY Select Status
WRITE_PHY_SEL = 0x22 //4022_AA00_0000_0000 Write Software PHY Select
};
// RX Control Register bits
enum AX88772_RXControl {
RXCTL_PROMISCUOUS = 0x0001, //
RXCTL_ALL_MULTICAT = 0x0002, //
// RXCTL_SEP = 0x0004, // do not set it!
RXCTL_BROADCAST = 0x0008, //
RXCTL_MULTICAST = 0x0010, //
RXCTL_AP = 0x0020, //
RXCTL_START = 0x0080, //
RXCTL_USB_MFB = 0x0100 // Max Frame Burst TX on USB
};
// PHY IDs request answer data layout
struct AX88772_PhyIDs {
uint8 SecPhyID;
uint8 PriPhyID2;
} _PACKED;
// Medium state bits
enum AX88772_MediumState {
MEDIUM_STATE_FD = 0x0002,
MEDIUM_STATE_BIT2 = 0x0004, // must be always set
MEDIUM_STATE_RFC = 0x0010,
MEDIUM_STATE_TFC = 0x0020,
MEDIUM_STATE_PF_ON = 0x0040,
MEDIUM_STATE_PF_OFF = 0x0000,
MEDIUM_STATE_RE = 0x0100,
MEDIUM_STATE_PS_100 = 0x0200,
MEDIUM_STATE_PS_10 = 0x0000,
MEDIUM_STATE_SBP1 = 0x0800,
MEDIUM_STATE_SBP0 = 0x0000,
MEDIUM_STATE_SM_ON = 0x1000
};
// Monitor Mode bits
enum AX88772_MonitorMode {
MONITOR_MODE_MOM = 0x01,
MONITOR_MODE_RWLU = 0x02,
MONITOR_MODE_RWMP = 0x04,
MONITOR_MODE_US = 0x10
};
// General Purpose I/O Register
enum AX88772_GPIO {
GPIO_OO_0EN = 0x01,
GPIO_IO_0 = 0x02,
GPIO_OO_1EN = 0x04,
GPIO_IO_1 = 0x08,
GPIO_OO_2EN = 0x10,
GPIO_IO_2 = 0x20,
GPIO_RSE = 0x80
};
// Software Reset Register bits
enum AX88772_SoftwareReset {
SW_RESET_CLR = 0x00,
SW_RESET_RR = 0x01,
SW_RESET_RT = 0x02,
SW_RESET_PRTE = 0x04,
SW_RESET_PRL = 0x08,
SW_RESET_BZ = 0x10,
SW_RESET_IPRL = 0x20,
SW_RESET_IPPD = 0x40
};
// Software PHY Select Status
enum AX88772_SoftwarePHYSelStatus {
SW_PHY_SEL_STATUS_EXT = 0x00,
SW_PHY_SEL_STATUS_INT = 0x01,
SW_PHY_SEL_STATUS_ASEL = 0x02
};
// Notification data layout
struct AX88772_Notify {
uint8 btA1;
uint8 bt01;
uint8 btBB; // AX88772_BBState below
uint8 bt03;
uint16 regCCDD;
uint16 regEEFF;
} _PACKED;
// Link-State bits
enum AX88772_BBState {
LINK_STATE_PPLS = 0x01,
LINK_STATE_SPLS = 0x02,
LINK_STATE_FLE = 0x04,
LINK_STATE_MDINT = 0x08
};
const uint16 maxFrameSize = 1536;
AX88772Device::AX88772Device(usb_device device, const char *description)
: ASIXDevice(device, description)
{
fStatus = InitDevice();
}
status_t
AX88772Device::InitDevice()
{
fFrameSize = maxFrameSize;
fUseTRXHeader = true;
fReadNodeIDRequest = READ_NODEID;
fReadRXControlRequest = READ_RX_CONTROL;
fWriteRXControlRequest = WRITE_RX_CONTROL;
fPromiscuousBits = RXCTL_PROMISCUOUS;
fNotifyBufferLength = sizeof(AX88772_Notify);
fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
if (fNotifyBuffer == NULL) {
TRACE_ALWAYS("Error of allocating memory for notify buffer.\n");
return B_NO_MEMORY;
}
return B_OK;
}
status_t
AX88772Device::SetupDevice(bool deviceReplugged)
{
status_t result = ASIXDevice::SetupDevice(deviceReplugged);
if(result != B_OK) {
return result;
}
result = fMII.Init(fDevice,
SW_MII_OP, READ_MII, WRITE_MII,
READ_MII_OP_MODE, HW_MII_OP, READ_PHYID);
size_t actualLength = 0;
// enable GPIO2 - magic from FreeBSD's if_axe
uint16 GPIOs = GPIO_OO_2EN | GPIO_IO_2 | GPIO_RSE;
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_GPIOS, GPIOs, 0, 0, 0, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of wrinting GPIOs: %#010x\n", result);
return result;
}
// select PHY
bool useEmbeddedPHY = fMII.PHYID() == PHYIDEmbedded;
uint16 selectPHY = useEmbeddedPHY ?
SW_PHY_SEL_STATUS_INT : SW_PHY_SEL_STATUS_EXT;
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_PHY_SEL, selectPHY, 0, 0, 0, &actualLength);
snooze(10000);
TRACE("Selecting %s PHY[%#02x].\n",
useEmbeddedPHY ? "embedded" : "external", selectPHY);
if(result != B_OK) {
TRACE_ALWAYS("Error of selecting PHY:%#010x\n", result);
return result;
}
struct SWReset {
uint16 reset;
bigtime_t delay;
} resetCommands[] = {
// EMBEDDED PHY
// power down and reset state, pin reset state
{ SW_RESET_CLR, 60000 },
// power down/reset state, pin operating state
{ SW_RESET_PRL | SW_RESET_IPPD, 150000 },
// power up, reset
{ SW_RESET_PRL, 0 },
// power up, operating
{ SW_RESET_PRL | SW_RESET_IPRL, 0 },
// EXTERNAL PHY
// power down/reset state, pin operating state
{ SW_RESET_PRL | SW_RESET_IPPD, 0 }
};
size_t from = useEmbeddedPHY ? 0 : 4;
size_t to = useEmbeddedPHY ? 3 : 4;
for(size_t i = from; i <= to; i++) {
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_SOFT_RESET, resetCommands[i].reset,
0, 0, 0, &actualLength);
snooze(resetCommands[i].delay);
if(result != B_OK) {
TRACE_ALWAYS("Error of SW reset command %d:[%#04x]: %#010x\n",
i, resetCommands[i].reset, result);
return result;
}
}
snooze(150000);
result = WriteRXControlRegister(0);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result);
return result;
}
result = fMII.SetupPHY();
if(result != B_OK) {
return result;
}
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_MEDIUM_MODE,
MEDIUM_STATE_FD | MEDIUM_STATE_BIT2 |
MEDIUM_STATE_RFC| MEDIUM_STATE_TFC |
MEDIUM_STATE_RE | MEDIUM_STATE_PS_100,
0, 0, 0, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of setting medium mode: %#010x\n", result);
}
TRACE_RET(result);
return result;
}
status_t
AX88772Device::StartDevice()
{
size_t actualLength = 0;
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
WRITE_IPGS, 0, 0, sizeof(fIPG), fIPG, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing IPGs:%#010x\n", result);
return result;
}
if(actualLength != sizeof(fIPG)) {
TRACE_ALWAYS("Mismatch of written IPGs data. "
"%d bytes of %d written.\n", actualLength, sizeof(fIPG));
}
uint16 rxcontrol = RXCTL_START | RXCTL_MULTICAST;
result = WriteRXControlRegister(rxcontrol);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", rxcontrol, result);
}
TRACE_RET(result);
return result;
}
status_t
AX88772Device::OnNotify(uint32 actualLength)
{
if (actualLength < sizeof(AX88772_Notify)) {
TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n",
actualLength, sizeof(AX88772_Notify));
return B_BAD_DATA;
}
AX88772_Notify *notification = (AX88772_Notify *)fNotifyBuffer;
if(notification->btA1 != 0xa1) {
TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n",
notification->btA1);
}
uint phyIndex = 0;
bool linkIsUp = fHasConnection;
switch(fMII.ActivePHY()) {
case PrimaryPHY:
phyIndex = 1;
linkIsUp = (notification->btBB & LINK_STATE_PPLS) == LINK_STATE_PPLS;
break;
case SecondaryPHY:
phyIndex = 2;
linkIsUp = (notification->btBB & LINK_STATE_SPLS) == LINK_STATE_SPLS;
break;
default:
case CurrentPHY:
TRACE_ALWAYS("Error: PHY is not initialized.\n");
return B_NO_INIT;
}
bool linkStateChange = linkIsUp != fHasConnection;
fHasConnection = linkIsUp;
if(linkStateChange) {
TRACE("Link state of PHY%d has been changed to '%s'\n",
phyIndex, fHasConnection ? "up" : "down");
}
if (linkStateChange && fLinkStateChangeSem >= B_OK)
release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
return B_OK;
}
status_t
AX88772Device::GetLinkState(ether_link_state *linkState)
{
size_t actualLength = 0;
uint16 mediumStatus = 0;
status_t result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
READ_MEDIUM_STATUS, 0, 0, sizeof(mediumStatus),
&mediumStatus, &actualLength);
if(result != B_OK) {
TRACE_ALWAYS("Error of reading medium status:%#010x.\n", result);
return result;
}
if(actualLength != sizeof(mediumStatus)) {
TRACE_ALWAYS("Mismatch of reading medium status."
"Read %d bytes instead of %d\n",
actualLength, sizeof(mediumStatus));
}
TRACE_FLOW("Medium status is %#04x\n", mediumStatus);
linkState->quality = 1000;
linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0);
linkState->media |= (mediumStatus & MEDIUM_STATE_FD) ?
IFM_FULL_DUPLEX : IFM_HALF_DUPLEX;
linkState->speed = (mediumStatus & MEDIUM_STATE_PS_100) ? 100000 : 10000;
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 B_OK;
}
@@ -0,0 +1,29 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_AX88772_DEVICE_H_
#define _USB_AX88772_DEVICE_H_
#include "ASIXDevice.h"
class AX88772Device : public ASIXDevice {
public:
AX88772Device(usb_device device, const char *description);
protected:
status_t InitDevice();
virtual status_t SetupDevice(bool deviceReplugged);
virtual status_t StartDevice();
virtual status_t OnNotify(uint32 actualLength);
virtual status_t GetLinkState(ether_link_state *state);
};
#endif //_USB_AX88772_DEVICE_H_
@@ -0,0 +1,354 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
#include <lock.h> // for mutex
#else
#include "BeOSCompatibility.h" // for pseudo mutex
#endif
#include "Driver.h"
#include "Settings.h"
#include "AX88172Device.h"
#include "AX88772Device.h"
#include "AX88178Device.h"
int32 api_version = B_CUR_DRIVER_API_VERSION;
static const char *sDeviceBaseName = "net/usb_asix/";
ASIXDevice *gASIXDevices[MAX_DEVICES];
char *gDeviceNames[MAX_DEVICES + 1];
usb_module_info *gUSBModule = NULL;
mutex gDriverLock;
// auto-release helper class
class DriverSmartLock {
public:
DriverSmartLock() { mutex_lock(&gDriverLock); }
~DriverSmartLock() { mutex_unlock(&gDriverLock); }
};
ASIXDevice *
create_asix_device(usb_device device)
{
const usb_device_descriptor *deviceDescriptor
= gUSBModule->get_device_descriptor(device);
if (deviceDescriptor == NULL) {
TRACE_ALWAYS("Error of getting USB device descriptor.\n");
return NULL;
}
#define IDS(__vendor, __product) (((__vendor) << 16) | (__product))
switch(IDS(deviceDescriptor->vendor_id, deviceDescriptor->product_id)) {
// AX88172
case IDS(0x0b95, 0x1720): return new AX88172Device(device, "ASIX 88172 10/100");
case IDS(0x07b8, 0x420a): return new AX88172Device(device, "ABOCOM UF200");
case IDS(0x1189, 0x0893): return new AX88172Device(device, "Acer C&M EP-1427X-2");
case IDS(0x0557, 0x2009): return new AX88172Device(device, "ATEN UC-210T");
case IDS(0x08dd, 0x90ff): return new AX88172Device(device, "Billionton USB2AR");
case IDS(0x07aa, 0x0017): return new AX88172Device(device, "Corega USB2TX");
case IDS(0x2001, 0x1A00): return new AX88172Device(device, "D-Link DUB-E100");
case IDS(0x1631, 0x6200): return new AX88172Device(device, "GoodWay USB2Ethernet");
case IDS(0x04f1, 0x3008): return new AX88172Device(device, "JVC MP-PRX1");
case IDS(0x077b, 0x2226): return new AX88172Device(device, "LinkSys USB 2.0");
case IDS(0x0411, 0x003d): return new AX88172Device(device, "Melco LUA-U2-KTX");
case IDS(0x0846, 0x1040): return new AX88172Device(device, "NetGear USB 2.0 Ethernet");
case IDS(0x086e, 0x1920): return new AX88172Device(device, "System TALKS SGC-X2UL");
case IDS(0x6189, 0x182d): return new AX88172Device(device, "Sitecom LN-029");
// AX88772
case IDS(0x0b95, 0x7720): return new AX88772Device(device, "ASIX 88772 10/100");
case IDS(0x13b1, 0x0018): return new AX88772Device(device, "Linksys USB200M rev.2");
case IDS(0x2001, 0x3c05): return new AX88772Device(device, "D-Link DUB-E100 rev.B1");
case IDS(0x1557, 0x7720): return new AX88772Device(device, "OQO 01+ Ethernet");
case IDS(0x05ac, 0x1402): return new AX88772Device(device, "Apple A1277");
// AX88178
case IDS(0x0b95, 0x1780): return new AX88178Device(device, "ASIX 88178 10/100/1000");
case IDS(0x050d, 0x5055): return new AX88178Device(device, "Belkin F5D5055");
case IDS(0x04bb, 0x0930): return new AX88178Device(device, "I/O Data ETG-US2");
case IDS(0x1737, 0x0039): return new AX88178Device(device, "LinkSys 1000");
case IDS(0x14ea, 0xab11): return new AX88178Device(device, "Planex GU-1000T");
case IDS(0x0df6, 0x061c): return new AX88178Device(device, "Sitecom LN-028");
}
return NULL;
}
status_t
usb_asix_device_added(usb_device device, void **cookie)
{
*cookie = NULL;
DriverSmartLock driverLock; // released on exit
// check if this is a replug of an existing device first
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gASIXDevices[i] == NULL)
continue;
if (gASIXDevices[i]->CompareAndReattach(device) != B_OK)
continue;
TRACE("The device is plugged back. Use entry at %ld.\n", i);
*cookie = gASIXDevices[i];
return B_OK;
}
// no such device yet, create a new one
ASIXDevice *asixDevice = create_asix_device(device);
if (asixDevice == 0) {
return ENODEV;
}
status_t status = asixDevice->InitCheck();
if (status < B_OK) {
delete asixDevice;
return status;
}
status = asixDevice->SetupDevice(false);
if (status < B_OK) {
delete asixDevice;
return status;
}
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gASIXDevices[i] != NULL)
continue;
gASIXDevices[i] = asixDevice;
*cookie = asixDevice;
TRACE("New device is added at %ld.\n", i);
return B_OK;
}
// no space for the device
TRACE_ALWAYS("Error: no more device entries availble.\n");
delete asixDevice;
return B_ERROR;
}
status_t
usb_asix_device_removed(void *cookie)
{
DriverSmartLock driverLock; // released on exit
ASIXDevice *device = (ASIXDevice *)cookie;
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gASIXDevices[i] == device) {
if (device->IsOpen()) {
// the device will be deleted upon being freed
device->Removed();
} else {
gASIXDevices[i] = NULL;
delete device;
TRACE("Device at %ld deleted.\n", i);
}
break;
}
}
return B_OK;
}
//#pragma mark -
status_t
init_hardware()
{
return B_OK;
}
status_t
init_driver()
{
status_t status = get_module(B_USB_MODULE_NAME,
(module_info **)&gUSBModule);
if (status < B_OK)
return status;
load_settings();
TRACE_ALWAYS("%s\n", kVersion);
for (int32 i = 0; i < MAX_DEVICES; i++)
gASIXDevices[i] = NULL;
gDeviceNames[0] = NULL;
mutex_init(&gDriverLock, DRIVER_NAME"_devices");
static usb_notify_hooks notifyHooks = {
&usb_asix_device_added,
&usb_asix_device_removed
};
gUSBModule->register_driver(DRIVER_NAME, 0, 0, NULL);
gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
return B_OK;
}
void
uninit_driver()
{
gUSBModule->uninstall_notify(DRIVER_NAME);
mutex_lock(&gDriverLock);
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gASIXDevices[i]) {
delete gASIXDevices[i];
gASIXDevices[i] = NULL;
}
}
for (int32 i = 0; gDeviceNames[i]; i++) {
free(gDeviceNames[i]);
gDeviceNames[i] = NULL;
}
mutex_destroy(&gDriverLock);
put_module(B_USB_MODULE_NAME);
release_settings();
}
static status_t
usb_asix_open(const char *name, uint32 flags, void **cookie)
{
DriverSmartLock driverLock; // released on exit
*cookie = NULL;
status_t status = ENODEV;
int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10);
if (index >= 0 && index < MAX_DEVICES && gASIXDevices[index]) {
status = gASIXDevices[index]->Open(flags);
*cookie = gASIXDevices[index];
}
return status;
}
static status_t
usb_asix_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
ASIXDevice *device = (ASIXDevice *)cookie;
return device->Read((uint8 *)buffer, numBytes);
}
static status_t
usb_asix_write(void *cookie, off_t position, const void *buffer,
size_t *numBytes)
{
ASIXDevice *device = (ASIXDevice *)cookie;
return device->Write((const uint8 *)buffer, numBytes);
}
static status_t
usb_asix_control(void *cookie, uint32 op, void *buffer, size_t length)
{
ASIXDevice *device = (ASIXDevice *)cookie;
return device->Control(op, buffer, length);
}
static status_t
usb_asix_close(void *cookie)
{
ASIXDevice *device = (ASIXDevice *)cookie;
return device->Close();
}
static status_t
usb_asix_free(void *cookie)
{
ASIXDevice *device = (ASIXDevice *)cookie;
DriverSmartLock driverLock; // released on exit
status_t status = device->Free();
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gASIXDevices[i] == device) {
// the device is removed already but as it was open the
// removed hook has not deleted the object
gASIXDevices[i] = NULL;
delete device;
TRACE("Device at %ld deleted.\n", i);
break;
}
}
return status;
}
const char **
publish_devices()
{
for (int32 i = 0; gDeviceNames[i]; i++) {
free(gDeviceNames[i]);
gDeviceNames[i] = NULL;
}
DriverSmartLock driverLock; // released on exit
int32 deviceCount = 0;
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gASIXDevices[i] == NULL)
continue;
gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4);
if (gDeviceNames[deviceCount]) {
sprintf(gDeviceNames[deviceCount], "%s%ld", sDeviceBaseName, i);
TRACE("publishing %s\n", gDeviceNames[deviceCount]);
deviceCount++;
} else
TRACE_ALWAYS("Error: out of memory during allocating device name.\n");
}
gDeviceNames[deviceCount] = NULL;
return (const char **)&gDeviceNames[0];
}
device_hooks *
find_device(const char *name)
{
static device_hooks deviceHooks = {
usb_asix_open,
usb_asix_close,
usb_asix_free,
usb_asix_control,
usb_asix_read,
usb_asix_write,
NULL, /* select */
NULL /* deselect */
};
return &deviceHooks;
}
@@ -0,0 +1,48 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_ASIX_DRIVER_H_
#define _USB_ASIX_DRIVER_H_
#include <OS.h>
#include <KernelExport.h>
#include <Drivers.h>
#include <USB3.h>
#include <ether_driver.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define DRIVER_NAME "usb_asix"
#define MAX_DEVICES 8
const uint8 kInvalidRequest = 0xff;
const char* const kVersion = "ver.0.8.3";
extern usb_module_info *gUSBModule;
extern "C" {
status_t usb_asix_device_added(usb_device device, void **cookie);
status_t usb_asix_device_removed(void *cookie);
status_t init_hardware();
void uninit_driver();
const char **publish_devices();
device_hooks *find_device(const char *name);
}
#endif //_USB_ASIX_DRIVER_H_
@@ -0,0 +1,15 @@
SubDir HAIKU_TOP src add-ons kernel drivers network usb_asix ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders kernel net ;
KernelAddon usb_asix :
Driver.cpp
ASIXDevice.cpp
AX88172Device.cpp
AX88772Device.cpp
AX88178Device.cpp
Settings.cpp
MIIBus.cpp
;
@@ -0,0 +1,369 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#include "Driver.h"
#include "Settings.h"
#include "MIIBus.h"
#define MII_OUI(id1, id2) (((id1) << 6) | ((id2) >> 10))
#define MII_MODEL(id2) (((id2) & 0x03f0) >> 4)
#define MII_REV(id2) ((id2) & 0x000f)
MIIBus::MIIBus() : fDevice(0),
fSelectedPHY(CurrentPHY),
fSWOperationRequest(kInvalidRequest),
fReadValueRequest(kInvalidRequest),
fWriteValueRequest(kInvalidRequest),
fReadStatusRequest(kInvalidRequest),
fHWOperationRequest(kInvalidRequest),
fReadPHYIDsRequest(kInvalidRequest)
{
for(size_t i = 0; i < PHYsCount; i++) {
fPHYs[i] = PHYNotInstalled;
}
}
status_t
MIIBus::Init(usb_device device,
uint8 SWOperationRequest,
uint8 ReadValueRequest,
uint8 WriteValueRequest,
uint8 ReadStatusRequest,
uint8 HWOperationRequest,
uint8 ReadPHYIDsRequest)
{
fSWOperationRequest = SWOperationRequest;
fReadValueRequest = ReadValueRequest;
fWriteValueRequest = WriteValueRequest;
fReadStatusRequest = ReadStatusRequest;
fHWOperationRequest = HWOperationRequest;
fReadPHYIDsRequest = ReadPHYIDsRequest;
// reset to default state
fDevice = 0;
fSelectedPHY = CurrentPHY;
for(size_t i = 0; i < PHYsCount; i++) {
fPHYs[i] = PHYNotInstalled;
}
size_t actual_length = 0;
status_t result = gUSBModule->send_request(device,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
fReadPHYIDsRequest, 0, 0, sizeof(fPHYs), fPHYs, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Request of the PHYIDs failed:%#010x\n", result);
return result;
}
if(sizeof(fPHYs) != actual_length) {
TRACE_ALWAYS("Mismatch of reading %d PHYIDs bytes instead of %d.\n",
actual_length, sizeof(fPHYs));
}
TRACE("PHYIDs are:%#02x:%#02x\n", fPHYs[0], fPHYs[1]);
// simply tactic - we use first available PHY
if(PHYType(PrimaryPHY) != PHYNotInstalled) {
fSelectedPHY = PrimaryPHY;
} else
if(PHYType(SecondaryPHY) != PHYNotInstalled) {
fSelectedPHY = SecondaryPHY;
}
TRACE("PHYs are configured: Selected:%#02x; Primary:%#02x; Secondary:%#02x\n",
PHYID(CurrentPHY), PHYID(PrimaryPHY), PHYID(SecondaryPHY));
if(fSelectedPHY == CurrentPHY) {
TRACE_ALWAYS("No PHYs found!\n");
return B_ENTRY_NOT_FOUND;
}
fDevice = device;
return result;
}
status_t
MIIBus::SetupPHY()
{
uint16 control = 0;
status_t result = Read(MII_BMCR, &control);
if(result != B_OK) {
TRACE_ALWAYS("Error of reading control word:%#010x.\n", result);
return result;
}
TRACE("MII Control word is %#04x\n", control);
control &= ~BMCR_Isolate;
result = Write(MII_BMCR, control);
if(result != B_OK) {
TRACE_ALWAYS("Error of writing control word %#04x:%#010x.\n", control, result);
}
result = Write(MII_BMCR, BMCR_Reset);
if(result != B_OK) {
TRACE_ALWAYS("Error of resetting PHY:%#010x.\n", result);
}
uint16 id01 = 0, id02 = 0;
result = Read(MII_PHYID0, &id01);
if(result != B_OK) {
TRACE_ALWAYS("Error of reading PHY ID1:%#010x.\n", result);
}
result = Read(MII_PHYID1, &id02);
if(result != B_OK) {
TRACE_ALWAYS("Error of reading PHY ID2:%#010x.\n", result);
}
TRACE("MII Info: OUI:%04x; Model:%04x; rev:%02x.\n",
MII_OUI(id01, id02), MII_MODEL(id02), MII_REV(id02));
//Dump();
return result;
}
status_t
MIIBus::InitCheck()
{
if (fSelectedPHY == CurrentPHY) {
return B_ENTRY_NOT_FOUND;
}
if(fSWOperationRequest == kInvalidRequest ||
fReadValueRequest == kInvalidRequest ||
fWriteValueRequest == kInvalidRequest ||
fReadStatusRequest == kInvalidRequest ||
fHWOperationRequest == kInvalidRequest ||
fReadPHYIDsRequest == kInvalidRequest) {
return B_NO_INIT;
}
return B_OK;
}
uint8
MIIBus::PHYID(PHYIndex phyIndex /*= CurrentPHY*/)
{
if(phyIndex == CurrentPHY) {
return (fSelectedPHY == CurrentPHY ?
0 : fPHYs[fSelectedPHY]) & PHYIDMask;
}
return fPHYs[phyIndex] & PHYIDMask;
}
uint8
MIIBus::PHYType(PHYIndex phyIndex /*= CurrentPHY*/)
{
if(phyIndex == CurrentPHY) {
return (fSelectedPHY == CurrentPHY ?
PHYNotInstalled : fPHYs[fSelectedPHY]) & PHYTypeMask;
}
return fPHYs[phyIndex] & PHYTypeMask;
}
status_t
MIIBus::Read(uint16 miiRegister, uint16 *value, PHYIndex phyIndex /*= CurrentPHY*/)
{
status_t result = InitCheck();
if(B_OK != result) {
TRACE_ALWAYS("Error: MII is not ready:%#010x\n", result);
return result;
}
if(PHYType(phyIndex) == PHYNotInstalled) {
TRACE_ALWAYS("Error: Invalid PHY index:%#02x.\n", phyIndex);
return B_ENTRY_NOT_FOUND;
}
uint16 phyId = PHYID(phyIndex);
size_t actual_length = 0;
// switch to SW operation mode
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fSWOperationRequest, 0, 0, 0, 0, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result);
return result;
}
// read register value
status_t op_result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
fReadValueRequest, phyId, miiRegister,
sizeof(*value), value, &actual_length);
if(op_result != B_OK) {
TRACE_ALWAYS("Error of reading MII reg.%d at PHY%d:%#010x.\n",
miiRegister, phyId, op_result);
}
if(sizeof(*value) != actual_length) {
TRACE_ALWAYS("Mismatch of reading MII reg.%d at PHY %d. "
"Read %d bytes instead of %d.\n",
miiRegister, phyId, actual_length, sizeof(*value));
}
// switch to HW operation mode
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fHWOperationRequest, 0, 0, 0, 0, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result);
}
return op_result;
}
status_t
MIIBus::Write(uint16 miiRegister, uint16 value, PHYIndex phyIndex /*= CurrentPHY*/)
{
size_t actual_length = 0;
status_t result = InitCheck();
if(B_OK != result) {
TRACE_ALWAYS("Error: MII is not ready:%#010x\n", result);
return result;
}
if(PHYType(phyIndex) == PHYNotInstalled) {
TRACE_ALWAYS("Error: Invalid PHY index:%#02x\n", phyIndex);
return B_ENTRY_NOT_FOUND;
}
uint16 phyId = PHYID(phyIndex);
// switch to SW operation mode
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fSWOperationRequest, 0, 0, 0, 0, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result);
return result;
}
// write register value
status_t op_result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fWriteValueRequest, phyId, miiRegister,
sizeof(value), &value, &actual_length);
if(op_result != B_OK) {
TRACE_ALWAYS("Error of writing MII reg.%d at PHY %d:%#010x.\n",
miiRegister, phyId, op_result);
}
if(sizeof(value) != actual_length) {
TRACE_ALWAYS("Mismatch of writing MII reg.%d at PHY %d."
"Write %d bytes instead of %d.\n",
miiRegister, phyId, actual_length, sizeof(value));
}
// switch to HW operation mode
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fHWOperationRequest, 0, 0, 0, 0, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result);
}
return op_result;
}
status_t
MIIBus::Status(uint16 *status, PHYIndex phyIndex /*= CurrentPHY*/)
{
return Read(MII_BMSR, status, phyIndex);
}
status_t
MIIBus::Dump()
{
status_t result = InitCheck();
if(B_OK != result) {
TRACE_ALWAYS("Error: MII is not ready:%#010x.\n", result);
return result;
}
if(PHYType(CurrentPHY) == PHYNotInstalled) {
TRACE_ALWAYS("Error: Current PHY index is invalid!\n");
return B_ENTRY_NOT_FOUND;
}
uint16 phyId = PHYID(CurrentPHY);
size_t actual_length = 0;
// switch to SW operation mode
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fSWOperationRequest, 0, 0, 0, 0, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result);
return result;
}
uint8 regs[] = { MII_BMCR, MII_BMSR,
MII_PHYID0, MII_PHYID1,
MII_ANAR, MII_ANLPAR/*, MII_ANER*/};
uint16 value = 0;
for(size_t i = 0; i < sizeof(regs)/ sizeof(regs[0]); i++) {
// read register value
status_t op_result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
fReadValueRequest, phyId, regs[i],
sizeof(value), &value, &actual_length);
if(op_result != B_OK) {
TRACE_ALWAYS("Error of reading MII reg.%d at PHY%d:%#010x.\n",
regs[i], phyId, op_result);
}
if(sizeof(value) != actual_length) {
TRACE_ALWAYS("Mismatch of reading MII reg.%d at PHY%d."
" Read %d bytes instead of %d.\n",
regs[i], phyId, actual_length, sizeof(value));
}
TRACE_ALWAYS("MII reg: %d has %#04x\n", regs[i], value);
}
// switch to HW operation mode
result = gUSBModule->send_request(fDevice,
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
fHWOperationRequest, 0, 0, 0, 0, &actual_length);
if(result != B_OK) {
TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result);
}
return result;
}
@@ -0,0 +1,143 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_MII_BUS_H_
#define _USB_MII_BUS_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
};
enum MII_BMCR {
BMCR_Reset = 0x8000,
BMCR_Loopback = 0x4000,
BMCR_SpeedSelection = 0x2000,
BMCR_ANegEnabled = 0x1000,
BMCR_PowerDown = 0x0800,
BMCR_Isolate = 0x0400,
BMCR_ANegRestart = 0x0200,
BMCR_FullDuplex = 0x0100,
BMCR_CollTest = 0x0080
};
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
};
// index used to different PHY on MII bus
enum PHYIndex {
CurrentPHY = -1, // currently selected PHY.
// Internally used as default index in case on PHYs found.
SecondaryPHY = 0, // secondary PHY
PrimaryPHY = 1, // primary PHY
PHYsCount = 2 // maximal count of PHYs on bus
};
// PHY type and id constants and masks.
enum PHYType {
PHYTypeMask = 0xe0, // mask for PHY type bits
PHYNormal = 0x00, // 10/100 Ethernet PHY (Link reports as normal case)
PHYLinkAState = 0x80, // Special case 1 (Link reports is always active)
PHYGigabit = 0x20, // Gigabit Ethernet PHY on AX88178
PHYNotInstalled = 0xe0, // non-supported PHY
PHYIDMask = 0x1f, // mask for PHY ID bits
PHYIDEmbedded = 0x10 // id for embedded PHY on AX88772
};
class MIIBus {
public:
MIIBus();
status_t Init(usb_device device,
uint8 SWOperationRequest,
uint8 ReadValueRequest,
uint8 WriteValueRequest,
uint8 ReadStatusRequest,
uint8 HWOperationRequest,
uint8 ReadPHYIDsRequest);
status_t InitCheck();
status_t SetupPHY();
uint8 PHYID(PHYIndex phyIndex = CurrentPHY);
uint8 PHYType(PHYIndex phyIndex = CurrentPHY);
PHYIndex ActivePHY() { return fSelectedPHY; }
status_t Read(uint16 miiRegister, uint16 *value, PHYIndex phyIndex = CurrentPHY);
status_t Write(uint16 miiRegister, uint16 value, PHYIndex phyIndex = CurrentPHY);
status_t Status(uint16 *status, PHYIndex phyIndex = CurrentPHY);
status_t Dump();
private:
usb_device fDevice;
uint8 fPHYs[PHYsCount];
PHYIndex fSelectedPHY;
uint8 fSWOperationRequest;
uint8 fReadValueRequest;
uint8 fWriteValueRequest;
uint8 fReadStatusRequest;
uint8 fHWOperationRequest;
uint8 fReadPHYIDsRequest;
};
#endif //_USB_MII_BUS_H_
@@ -0,0 +1,111 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#include <lock.h> // for mutex
#include "Settings.h"
bool gTraceOn = false;
bool gTruncateLogFile = false;
bool gAddTimeStamp = true;
bool gTraceFlow = false;
static char *gLogFilePath = NULL;
mutex gLogLock;
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);
gTraceFlow = get_driver_boolean_parameter(handle, "trace_flow", gTraceFlow, true);
gTruncateLogFile = get_driver_boolean_parameter(handle, "truncate_logfile",
gTruncateLogFile, 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 usb_asix_trace(bool force, const char* func, const char *fmt, ...)
{
if(!(force || gTraceOn)) {
return;
}
va_list arg_list;
static char *prefix = "\33[33m"DRIVER_NAME":\33[0m";
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);
}
@@ -0,0 +1,34 @@
/*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* Heavily based on code of the
* Driver for USB Ethernet Control Model devices
* Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_ASIX_SETTINGS_H_
#define _USB_ASIX_SETTINGS_H_
#include <driver_settings.h>
#include "Driver.h"
void load_settings();
void release_settings();
void usb_asix_trace(bool force, const char *func, const char *fmt, ...);
#define TRACE(x...) usb_asix_trace(false, __func__, x)
#define TRACE_ALWAYS(x...) usb_asix_trace(true, __func__, x)
extern bool gTraceFlow;
#define TRACE_FLOW(x...) usb_asix_trace(gTraceFlow, NULL, x)
#define TRACE_RET(result) usb_asix_trace(false, __func__, \
"Returns:%#010x\n", result);
#endif /*_USB_ASIX_SETTINGS_H_*/
@@ -0,0 +1,35 @@
##
## ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
## Copyright (c) 2008 S.Zharski <[email protected]>
## Distributed under the terms of the MIT license.
##
## trace [on|off] - activate additional tracing.
## default value: off
# trace on
## logfile [full path to private log file]
## default path value: /var/log/usb_asix.log
## if disabled - all output goes to syslog
#logfile /var/log/usb_asix.log
## reset_logfile [on|off] - truncate private log file on driver/system restart
## default value: off
##
# reset_logfile off
## add_timestamp [on|off] - add time of writing the string in private log file.
## default value: on
##
# add_timestamp off
## trace_flow [on|off] - activate data flow tracing. Statistic about of
## transferred data amount and media state.
## default value: off
# trace_flow on