usb_asix: Delete.
Now provided by asix88xxx instead.
This commit is contained in:
@@ -1,757 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 "ASIXDevice.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ASIXVendorRequests.h"
|
||||
#include "Driver.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
// 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, DeviceInfo& deviceInfo)
|
||||
:
|
||||
fDevice(device),
|
||||
fStatus(B_ERROR),
|
||||
fOpen(false),
|
||||
fRemoved(false),
|
||||
fHasConnection(false),
|
||||
fNonBlocking(false),
|
||||
fInsideNotify(0),
|
||||
fFrameSize(0),
|
||||
fNotifyEndpoint(0),
|
||||
fReadEndpoint(0),
|
||||
fWriteEndpoint(0),
|
||||
fActualLengthRead(0),
|
||||
fActualLengthWrite(0),
|
||||
fStatusRead(B_OK),
|
||||
fStatusWrite(B_OK),
|
||||
fNotifyReadSem(-1),
|
||||
fNotifyWriteSem(-1),
|
||||
fNotifyBuffer(NULL),
|
||||
fNotifyBufferLength(0),
|
||||
fLinkStateChangeSem(-1),
|
||||
fUseTRXHeader(false),
|
||||
fReadNodeIDRequest(kInvalidRequest)
|
||||
{
|
||||
fDeviceInfo = deviceInfo;
|
||||
|
||||
fIPG[0] = 0x15;
|
||||
fIPG[1] = 0x0c;
|
||||
fIPG[2] = 0x12;
|
||||
|
||||
memset(&fMACAddress, 0, sizeof(fMACAddress));
|
||||
|
||||
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_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// the device pushes packets 16bit aligned
|
||||
if (fActualLengthRead - sizeof(TRXHeader) > header.Length()
|
||||
+ (header.Length() % 2u)) {
|
||||
TRACE_ALWAYS("MISMATCH of the frame length: hdr %d; received:%d\n",
|
||||
header.Length(), fActualLengthRead - sizeof(TRXHeader));
|
||||
} else if (fActualLengthRead - sizeof(TRXHeader) < header.Length()) {
|
||||
TRACE_ALWAYS("Error: received too little data: 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_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
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, (ether_address_t*)buffer);
|
||||
|
||||
case ETHER_REMMULTI:
|
||||
TRACE("ETHER_REMMULTI\n");
|
||||
return ModifyMulticastTable(false, (ether_address_t*)buffer);
|
||||
|
||||
case ETHER_SET_LINK_STATE_SEM:
|
||||
fLinkStateChangeSem = *(sem_id *)buffer;
|
||||
return B_OK;
|
||||
|
||||
case ETHER_GET_LINK_STATE:
|
||||
return GetLinkState((ether_link_state *)buffer);
|
||||
|
||||
default:
|
||||
TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op);
|
||||
}
|
||||
|
||||
return B_DEV_INVALID_IOCTL;
|
||||
}
|
||||
|
||||
|
||||
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 != fDeviceInfo.VendorId()
|
||||
&& deviceDescriptor->product_id != fDeviceInfo.ProductId()) {
|
||||
// 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_MASK)
|
||||
== USB_ENDPOINT_ADDR_DIR_IN) {
|
||||
readEndpoint = ep;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK)
|
||||
== 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, READ_RX_CONTROL,
|
||||
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, WRITE_RX_CONTROL,
|
||||
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 |= RXCTL_PROMISCUOUS;
|
||||
else
|
||||
rxcontrol &= ~RXCTL_PROMISCUOUS;
|
||||
|
||||
result = WriteRXControlRegister(rxcontrol);
|
||||
|
||||
if (result != B_OK ) {
|
||||
TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n",
|
||||
rxcontrol, result);
|
||||
}
|
||||
|
||||
TRACE_RET(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
ASIXDevice::EthernetCRC32(const uint8* buffer, size_t length)
|
||||
{
|
||||
uint32 result = 0xffffffff;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
uint8 data = *buffer++;
|
||||
for (int bit = 0; bit < 8; bit++, data >>= 1) {
|
||||
uint32 carry = ((result & 0x80000000) ? 1 : 0) ^ (data & 0x01);
|
||||
result <<= 1;
|
||||
if (carry != 0)
|
||||
result = (result ^ 0x04c11db6) | carry;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ASIXDevice::ModifyMulticastTable(bool join, ether_address_t* group)
|
||||
{
|
||||
char groupName[6 * 3 + 1] = { 0 };
|
||||
sprintf(groupName, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
group->ebyte[0], group->ebyte[1], group->ebyte[2],
|
||||
group->ebyte[3], group->ebyte[4], group->ebyte[5]);
|
||||
TRACE("%s multicast group %s\n", join ? "Joining" : "Leaving", groupName);
|
||||
|
||||
uint32 hash = EthernetCRC32(group->ebyte, 6);
|
||||
bool isInTable = fMulticastHashes.Find(hash) != fMulticastHashes.End();
|
||||
|
||||
if (isInTable && join)
|
||||
return B_OK; // already listed - nothing to do
|
||||
|
||||
if (!isInTable && !join) {
|
||||
TRACE_ALWAYS("Cannot leave unlisted multicast group %s!\n", groupName);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
const size_t hashLength = 8;
|
||||
uint8 hashTable[hashLength] = { 0 };
|
||||
|
||||
if (join)
|
||||
fMulticastHashes.PushBack(hash);
|
||||
else
|
||||
fMulticastHashes.Remove(hash);
|
||||
|
||||
for (int32 i = 0; i < fMulticastHashes.Count(); i++) {
|
||||
uint32 hash = fMulticastHashes[i] >> 26;
|
||||
hashTable[hash / 8] |= 1 << (hash % 8);
|
||||
}
|
||||
|
||||
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 (fMulticastHashes.Count() > 0)
|
||||
rxcontrol |= RXCTL_MULTICAST;
|
||||
else
|
||||
rxcontrol &= ~RXCTL_MULTICAST;
|
||||
|
||||
// write multicast hash table
|
||||
size_t actualLength = 0;
|
||||
result = gUSBModule->send_request(fDevice,
|
||||
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_MF_ARRAY,
|
||||
0, 0, hashLength, hashTable, &actualLength);
|
||||
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("Error writing hash table in MAR: %#010x.\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (actualLength != hashLength)
|
||||
TRACE_ALWAYS("Incomplete writing of hash table: %d bytes of %d\n",
|
||||
actualLength, hashLength);
|
||||
|
||||
result = WriteRXControlRegister(rxcontrol);
|
||||
if (result != B_OK)
|
||||
TRACE_ALWAYS("Error writing %#02X to RXC:%#010x.\n", rxcontrol, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ASIXDevice::_ReadCallback(void *cookie, int32 status, void *data,
|
||||
size_t 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,
|
||||
size_t 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,
|
||||
size_t 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);
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 <ether_driver.h>
|
||||
#include <util/Vector.h>
|
||||
|
||||
#include "Driver.h"
|
||||
#include "MIIBus.h"
|
||||
|
||||
|
||||
struct DeviceInfo {
|
||||
uint16 fIds[2];
|
||||
|
||||
enum Type {
|
||||
AX88172 = 0,
|
||||
AX88772 = 1,
|
||||
AX88178 = 2,
|
||||
AX88772A = 3,
|
||||
AX88772B = 4
|
||||
} fType;
|
||||
|
||||
const char* fName;
|
||||
|
||||
inline uint16 VendorId() { return fIds[0]; }
|
||||
inline uint16 ProductId() { return fIds[1]; }
|
||||
inline uint32 Key() { return fIds[0] << 16 | fIds[1]; }
|
||||
};
|
||||
|
||||
|
||||
class ASIXDevice {
|
||||
public:
|
||||
ASIXDevice(usb_device device, DeviceInfo& devInfo);
|
||||
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, size_t actualLength);
|
||||
static void _WriteCallback(void *cookie, int32 status,
|
||||
void *data, size_t actualLength);
|
||||
static void _NotifyCallback(void *cookie, int32 status,
|
||||
void *data, size_t 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);
|
||||
uint32 EthernetCRC32(const uint8* buffer, size_t length);
|
||||
virtual status_t ModifyMulticastTable(bool add,
|
||||
ether_address_t* group);
|
||||
virtual status_t ReadMACAddress(ether_address_t *address);
|
||||
status_t ReadRXControlRegister(uint16 *rxcontrol);
|
||||
status_t WriteRXControlRegister(uint16 rxcontrol);
|
||||
|
||||
// device info
|
||||
usb_device fDevice;
|
||||
DeviceInfo fDeviceInfo;
|
||||
ether_address_t fMACAddress;
|
||||
|
||||
// state tracking
|
||||
status_t fStatus;
|
||||
bool fOpen;
|
||||
bool fRemoved;
|
||||
bool fHasConnection;
|
||||
bool fNonBlocking;
|
||||
int32 fInsideNotify;
|
||||
|
||||
// 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;
|
||||
sem_id fLinkStateChangeSem;
|
||||
|
||||
// MII bus handler
|
||||
MIIBus fMII;
|
||||
|
||||
// connection data
|
||||
bool fUseTRXHeader;
|
||||
uint8 fIPG[3];
|
||||
uint8 fReadNodeIDRequest;
|
||||
Vector<uint32> fMulticastHashes;
|
||||
};
|
||||
|
||||
#endif // _USB_ASIX_DEVICE_H_
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2011 S.Zharski <[email protected]>
|
||||
* Distributed under the terms of the MIT license.
|
||||
*
|
||||
*/
|
||||
#ifndef _ASIX_VENDOR_REQUESTS_H_
|
||||
#define _ASIX_VENDOR_REQUESTS_H_
|
||||
|
||||
|
||||
// USB Vendor Requests used by all chip types
|
||||
// For chip-spercific information look into
|
||||
// corresponding AX88***Device.cpp files.
|
||||
enum ASIXVendorRequests {
|
||||
READ_RXTX_SRAM = 0x02,
|
||||
WRITE_RXTX_SRAM = 0x03, // AX88178-772
|
||||
WRITE_RX_SRAM = 0x03, // AX88172
|
||||
WRITE_TX_SRAM = 0x04, // AX88172
|
||||
SW_MII_OP = 0x06,
|
||||
READ_MII = 0x07,
|
||||
WRITE_MII = 0x08,
|
||||
READ_MII_OP_MODE = 0x09, // AX88172-772
|
||||
READ_MII_STATUS = 0x09, // AX88178
|
||||
HW_MII_OP = 0x0A,
|
||||
READ_SROM = 0x0B,
|
||||
WRITE_SROM = 0x0C,
|
||||
WRITE_SROM_ENABLE = 0x0D,
|
||||
WRITE_SROM_DISABLE = 0x0E,
|
||||
READ_RX_CONTROL = 0x0F,
|
||||
WRITE_RX_CONTROL = 0x10,
|
||||
READ_IPGS = 0x11,
|
||||
WRITE_IPGS = 0x12, // AX88178-772
|
||||
WRITE_IPG0 = 0x12, // AX88172
|
||||
WRITE_IPG1 = 0x13, // AX88172
|
||||
WRITE_IPG2 = 0x14, // AX88172
|
||||
READ_NODEID = 0x13, // AX88178-772
|
||||
WRITE_NODEID = 0x14, // AX88178-772
|
||||
READ_MF_ARRAY = 0x15,
|
||||
WRITE_MF_ARRAY = 0x16,
|
||||
READ_TEST = 0x17, // AX88178-772
|
||||
READ_NODEID_AX88172 = 0x17, // AX88172
|
||||
WRITE_NODEID_AX88172 = 0x18, // AX88172
|
||||
READ_PHYID = 0x19,
|
||||
READ_MEDIUM_STATUS = 0x1A,
|
||||
WRITE_MEDIUM_MODE = 0x1B,
|
||||
GET_MONITOR_MODE = 0x1C,
|
||||
SET_MONITOR_MODE = 0x1D,
|
||||
READ_GPIOS = 0x1E,
|
||||
WRITE_GPIOS = 0x1F,
|
||||
WRITE_SOFT_RESET = 0x20, // AX88178-772
|
||||
READ_PHY_SEL_STATE = 0x21, // AX88772
|
||||
WRITE_PHY_SEL = 0x22, // AX88772
|
||||
READ_MIIS_IF_STATE = 0x21, // AX88178
|
||||
WRITE_MIIS_IF_STATE = 0x22, // AX88178
|
||||
WRITE_RXCONTROL_CFG = 0x2A // AX88772B
|
||||
};
|
||||
|
||||
|
||||
// RX Control Register bits
|
||||
enum ASIXRXControl {
|
||||
RXCTL_PROMISCUOUS = 0x0001,
|
||||
RXCTL_ALL_MULTICAT = 0x0002,
|
||||
RXCTL_UNICAST = 0x0004, // AX88172
|
||||
RXCTL_SEP = 0x0004, // AX88772-178
|
||||
RXCTL_BROADCAST = 0x0008,
|
||||
RXCTL_MULTICAST = 0x0010,
|
||||
RXCTL_AP = 0x0020, // AX88772-178
|
||||
RXCTL_START = 0x0080,
|
||||
RXCTL_USB_MFB_2048 = 0x0000, // AX88772-178
|
||||
RXCTL_USB_MFB_4096 = 0x0100, // AX88772-178
|
||||
RXCTL_USB_MFB_8192 = 0x0200, // AX88772-178
|
||||
RXCTL_USB_MFB_MAX = 0x0300, // aka 16384 + mask AX88772-178
|
||||
RXCTL_LOOPBACK = 0x1000, // AX88772A / AX88772B
|
||||
};
|
||||
|
||||
|
||||
#endif // _ASIX_VENDOR_REQUESTS_H_
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 "AX88172Device.h"
|
||||
|
||||
#include <net/if_media.h>
|
||||
|
||||
#include "ASIXVendorRequests.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
// Most of vendor requests for all supported chip types use the same
|
||||
// constants (see ASIXVendorRequests.h) but the layout of request data
|
||||
// may be slightly diferrent for specific chip type. Below is a quick
|
||||
// reference for AX88172 vendor requests data layout.
|
||||
//
|
||||
// READ_RXTX_SRAM, // C0 02 XX YY 0M 00 0200 Read Rx/Tx SRAM
|
||||
// M = 0 : Rx, M=1 : Tx
|
||||
// WRITE_RX_SRAM, // 40 03 XX YY PP QQ 0000 Write Rx SRAM
|
||||
// WRITE_TX_SRAM, // 40 04 XX YY PP QQ 0000 Write Tx SRAM
|
||||
// SW_MII_OP, // 40 06 00 00 00 00 0000 Software MII Operation
|
||||
// READ_MII, // C0 07 PI 00 RG 00 0200 Read MII Register
|
||||
// WRITE_MII, // 40 08 PI 00 RG 00 0200 Write MII Register
|
||||
// READ_MII_OP_MODE, // C0 09 00 00 00 00 0100 Read MII Operation Mode
|
||||
// HW_MII_OP, // 40 0A 00 00 00 00 0000 Hardware MII Operation
|
||||
// READ_SROM, // C0 0B DR 00 00 00 0200 Read SROM
|
||||
// WRITE_SROM, // 40 0C DR 00 MM SS 0000 Write SROM
|
||||
// WRITE_SROM_ENABLE, // 40 0D 00 00 00 00 0000 Write SROM Enable
|
||||
// WRITE_SROM_DISABLE, // 40 0E 00 00 00 00 0000 Write SROM Disable
|
||||
// READ_RX_CONTROL, // C0 0F 00 00 00 00 0200 Read Rx Control Register
|
||||
// WRITE_RX_CONTROL, // 40 10 RR 00 00 00 0000 Write Rx Control Register
|
||||
// READ_IPGS, // C0 11 00 00 00 00 0300 Read IPG/IPG1/IPG2 Register
|
||||
// WRITE_IPG0, // 40 12 II 00 00 00 0000 Write IPG Register
|
||||
// WRITE_IPG1, // 40 13 II 00 00 00 0000 Write IPG1 Register
|
||||
// WRITE_IPG2, // 40 14 II 00 00 00 0000 Write IPG2 Register
|
||||
// READ_MF_ARRAY, // C0 15 00 00 00 00 0800 Read Multi-Filter Array
|
||||
// WRITE_MF_ARRAY, // 40 16 00 00 00 00 0800 Write Multi-Filter Array
|
||||
// READ_NODEID, // C0 17 00 00 00 00 0600 Read Node ID
|
||||
// WRITE_NODEID, //
|
||||
// READ_PHYID, // C0 19 00 00 00 00 0200 Read Ethernet/HomePNA PhyID
|
||||
// READ_MEDIUM_STATUS, // C0 1A 00 00 00 00 0100 Read Medium Status
|
||||
// WRITE_MEDIUM_MODE, // 40 1B MM 00 00 00 0000 Write Medium Mode
|
||||
// GET_MONITOR_MODE, // C0 1C 00 00 00 00 0100 Get Monitor Mode Status
|
||||
// SET_MONITOR_MODE, // 40 1D MM 00 00 00 0000 Set Monitor Mode On/Off
|
||||
// READ_GPIOS, // C0 1E 00 00 00 00 0100 Read GPIOs
|
||||
// WRITE_GPIOS, // 40 1F MM 00 00 00 0000 Write GPIOs
|
||||
|
||||
// RX Control Register bits
|
||||
// RXCTL_PROMISCUOUS, // forward all frames up to the host
|
||||
// RXCTL_ALL_MULTICAT, // forward all multicast frames up to the host
|
||||
// RXCTL_UNICAST, // ???
|
||||
// RXCTL_BROADCAST, // forward broadcast frames up to the host
|
||||
// RXCTL_MULTICAST, // forward all multicast frames that are
|
||||
// matching to multicast filter up to the host
|
||||
// RXCTL_START, // ethernet MAC start operating
|
||||
|
||||
|
||||
// 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, DeviceInfo& deviceInfo)
|
||||
:
|
||||
ASIXDevice(device, deviceInfo)
|
||||
{
|
||||
fStatus = InitDevice();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AX88172Device::InitDevice()
|
||||
{
|
||||
fFrameSize = maxFrameSize;
|
||||
|
||||
fReadNodeIDRequest = READ_NODEID_AX88172;
|
||||
|
||||
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);
|
||||
|
||||
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_UNICAST | RXCTL_BROADCAST;
|
||||
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)
|
||||
? 100000000 : 10000000;
|
||||
|
||||
TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n",
|
||||
(linkState->media & IFM_ACTIVE) ? "active" : "inactive",
|
||||
linkState->speed / 1000000,
|
||||
(linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
|
||||
return B_OK;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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_AX88172_DEVICE_H_
|
||||
#define _USB_AX88172_DEVICE_H_
|
||||
|
||||
|
||||
#include "ASIXDevice.h"
|
||||
|
||||
|
||||
class AX88172Device : public ASIXDevice {
|
||||
public:
|
||||
AX88172Device(usb_device device, DeviceInfo& info);
|
||||
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_
|
||||
|
||||
@@ -1,430 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 "AX88178Device.h"
|
||||
|
||||
#include <net/if_media.h>
|
||||
|
||||
#include "ASIXVendorRequests.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
// Most of vendor requests for all supported chip types use the same
|
||||
// constants (see ASIXVendorRequests.h) but the layout of request data
|
||||
// may be slightly diferrent for specific chip type. Below is a quick
|
||||
// reference for AX88178 vendor requests data layout.
|
||||
|
||||
// READ_RXTX_SRAM, //C002_AA0B_0C00_0800 Rx/Tx SRAM Read
|
||||
// WRITE_RXTX_SRAM, //4003_AA0B_0C00_0800 Rx/Tx SRAM Write
|
||||
// SW_MII_OP, //4006_0000_0000_0000 SW Serial Management Control
|
||||
// READ_MII, //c007_aa00_cc00_0200 PHY Read
|
||||
// WRITE_MII, //4008_aa00_cc00_0200 PHY Write
|
||||
// READ_MII_STATUS, //c009_0000_0000_0100 Serial Management Status
|
||||
// HW_MII_OP, //400a_0000_0000_0000 HW Serial Management Control
|
||||
// READ_SROM, //C00B_AA00_0000_0200 SROM Read
|
||||
// WRITE_SROM, //400C_AA00_CCDD_0000 SROM Write
|
||||
// WRITE_SROM_ENABLE, //400D_0000_0000_0000 SROM Write Enable
|
||||
// WRITE_SROM_DISABLE, //400E_0000_0000_0000 SROM Write Disable
|
||||
// READ_RX_CONTROL, //C00F_0000_0000_0200 Read Rx Control
|
||||
// WRITE_RX_CONTROL, //4010_AABB_0000_0000 Write Rx Control
|
||||
// READ_IPGS, //C011_0000_0000_0300 Read IPG/IPG1/IPG2 Register
|
||||
// WRITE_IPGS, //4012_AABB_CC00_0000 Write IPG/IPG1/IPG2 Register
|
||||
// READ_NODEID, //C013_0000_0000_0600 Read Node ID
|
||||
// WRITE_NODEID, //4014_0000_0000_0600 Write Node ID
|
||||
// READ_MF_ARRAY, //C015_0000_0000_0800 Read Multicast Filter Array
|
||||
// WRITE_MF_ARRAY, //4016_0000_0000_0800 Write Multicast Filter Array
|
||||
// READ_TEST, //4017_AA00_0000_0000 Write Test Register
|
||||
// READ_PHYID, //C019_0000_0000_0200 Read Ethernet/HomePNA PHY Address
|
||||
// READ_MEDIUM_STATUS, //C01A_0000_0000_0200 Read Medium Status
|
||||
// WRITE_MEDIUM_MODE, //401B_AABB_0000_0000 Write Medium Mode Register
|
||||
// GET_MONITOR_MODE, //C01C_0000_0000_0100 Read Monitor Mode Status
|
||||
// SET_MONITOR_MODE, //401D_AA00_0000_0000 Write Monitor Mode Register
|
||||
// READ_GPIOS, //C01E_0000_0000_0100 Read GPIOs Status
|
||||
// WRITE_GPIOS, //401F_AA00_0000_0000 Write GPIOs
|
||||
// WRITE_SOFT_RESET, //4020_AA00_0000_0000 Write Software Reset
|
||||
// READ_MIIS_IF_STATE, //C021_AA00_0000_0100 Read MII/GMII/RGMII Iface Status
|
||||
// WRITE_MIIS_IF_STATE, //4022_AA00_0000_0000 Write MII/GMII/RGMII Iface Control
|
||||
|
||||
// RX Control Register bits
|
||||
// RXCTL_PROMISCUOUS, // forward all frames up to the host
|
||||
// RXCTL_ALL_MULTICAT, // forward all multicast frames up to the host
|
||||
// RXCTL_SEP, // forward frames with CRC error up to the host
|
||||
// RXCTL_BROADCAST, // forward broadcast frames up to the host
|
||||
// RXCTL_MULTICAST, // forward multicast frames that are
|
||||
// matching to multicast filter up to the host
|
||||
// RXCTL_AP, // forward unicast frames that are matching
|
||||
// to multicast filter up to the host
|
||||
// RXCTL_START, // ethernet MAC start operating
|
||||
// RXCTL_USB_MFB, // 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, DeviceInfo& deviceInfo)
|
||||
:
|
||||
ASIXDevice(device, deviceInfo)
|
||||
{
|
||||
fStatus = InitDevice();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AX88178Device::InitDevice()
|
||||
{
|
||||
fFrameSize = maxFrameSize;
|
||||
fUseTRXHeader = true;
|
||||
|
||||
fReadNodeIDRequest = READ_NODEID;
|
||||
|
||||
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);
|
||||
|
||||
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_BROADCAST;
|
||||
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)
|
||||
? 100000000 : 10000000;
|
||||
linkState->speed = (mediumStatus & MEDIUM_STATE_GM)
|
||||
? 1000000000 : linkState->speed;
|
||||
|
||||
TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n",
|
||||
(linkState->media & IFM_ACTIVE) ? "active" : "inactive",
|
||||
linkState->speed / 1000000,
|
||||
(linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
|
||||
return B_OK;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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_AX88178_DEVICE_H_
|
||||
#define _USB_AX88178_DEVICE_H_
|
||||
|
||||
|
||||
#include "ASIXDevice.h"
|
||||
|
||||
|
||||
class AX88178Device : public ASIXDevice {
|
||||
public:
|
||||
AX88178Device(usb_device device, DeviceInfo& info);
|
||||
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_
|
||||
@@ -1,621 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 "AX88772Device.h"
|
||||
|
||||
#include <net/if_media.h>
|
||||
|
||||
#include "ASIXVendorRequests.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
// Most of vendor requests for all supported chip types use the same
|
||||
// constants (see ASIXVendorRequests.h) but the layout of request data
|
||||
// may be slightly diferrent for specific chip type. Below is a quick
|
||||
// reference for AX88772 vendor requests data layout.
|
||||
|
||||
// READ_RXTX_SRAM, //C002_AA0B_0C00_0800 Rx/Tx SRAM Read
|
||||
// WRITE_RXTX_SRAM, //4003_AA0B_0C00_0800 Rx/Tx SRAM Write
|
||||
// SW_MII_OP, //4006_0000_0000_0000 SW Serial Management Control
|
||||
// READ_MII, //c007_aa00_cc00_0200 PHY Read
|
||||
// WRITE_MII, //4008_aa00_cc00_0200 PHY Write
|
||||
// READ_MII_OP_MODE, //c009_0000_0000_0100 Serial Management Status
|
||||
// HW_MII_OP, //400a_0000_0000_0000 HW Serial Management Control
|
||||
// READ_SROM, //C00B_AA00_0000_0200 SROM Read
|
||||
// WRITE_SROM, //400C_AA00_CCDD_0000 SROM Write
|
||||
// WRITE_SROM_ENABLE, //400D_0000_0000_0000 SROM Write Enable
|
||||
// WRITE_SROM_DISABLE, //400E_0000_0000_0000 SROM Write Disable
|
||||
// READ_RX_CONTROL, //C00F_0000_0000_0200 Read Rx Control
|
||||
// WRITE_RX_CONTROL, //4010_AABB_0000_0000 Write Rx Control
|
||||
// READ_IPGS, //C011_0000_0000_0300 Read IPG/IPG1/IPG2 Register
|
||||
// WRITE_IPGS, //4012_AABB_CC00_0000 Write IPG/IPG1/IPG2 Register
|
||||
// READ_NODEID, //C013_0000_0000_0600 Read Node ID
|
||||
// WRITE_NODEID, //4014_0000_0000_0600 Write Node ID
|
||||
// READ_MF_ARRAY, //C015_0000_0000_0800 Read Multicast Filter Array
|
||||
// WRITE_MF_ARRAY, //4016_0000_0000_0800 Write Multicast Filter Array
|
||||
// READ_TEST, //4017_AA00_0000_0000 Write Test Register
|
||||
// READ_PHYID, //C019_0000_0000_0200 Read Ethernet/HomePNA PHY Address
|
||||
// READ_MEDIUM_STATUS, //C01A_0000_0000_0200 Read Medium Status
|
||||
// WRITE_MEDIUM_MODE, //401B_AABB_0000_0000 Write Medium Mode Register
|
||||
// GET_MONITOR_MODE, //C01C_0000_0000_0100 Read Monitor Mode Status
|
||||
// SET_MONITOR_MODE, //401D_AA00_0000_0000 Write Monitor Mode Register
|
||||
// READ_GPIOS, //C01E_0000_0000_0100 Read GPIOs Status
|
||||
// WRITE_GPIOS, //401F_AA00_0000_0000 Write GPIOs
|
||||
// WRITE_SOFT_RESET, //4020_AA00_0000_0000 Write Software Reset
|
||||
// READ_PHY_SEL_STATE, //C021_AA00_0000_0100 Read Software PHY Select Status
|
||||
// WRITE_PHY_SEL, //4022_AA00_0000_0000 Write Software PHY Select
|
||||
|
||||
// RX Control Register bits
|
||||
// RXCTL_PROMISCUOUS, // forward all frames up to the host
|
||||
// RXCTL_ALL_MULTICAT, // forward all multicast frames up to the host
|
||||
// RXCTL_SEP, // forward frames with CRC error up to the host
|
||||
// RXCTL_BROADCAST, // forward broadcast frames up to the host
|
||||
// RXCTL_MULTICAST, // forward multicast frames that are
|
||||
// matching to multicast filter up to the host
|
||||
// RXCTL_AP, // forward unicast frames that are matching
|
||||
// to multicast filter up to the host
|
||||
// RXCTL_START, // ethernet MAC start operating
|
||||
// RXCTL_USB_MFB, // 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,
|
||||
SW_PHY_SEL_STATUS_SS_MII = 0x04,
|
||||
SW_PHY_SEL_STATUS_SS_RVRS_MII = 0x08,
|
||||
SW_PHY_SEL_STATUS_SS_RVRS_GMII = 0x0C,
|
||||
SW_PHY_SEL_STATUS_SS_ENB = 0x10
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
// RX Control Register bits (772B)
|
||||
enum ASIX772RXControl {
|
||||
RXCTL_HDR_TYPE_0 = 0x0000,
|
||||
RXCTL_HDR_TYPE_1 = 0x0100,
|
||||
RXCTL_HDR_IPALIGN = 0x0200,
|
||||
RXCTL_ADD_CHKSUM = 0x0400,
|
||||
};
|
||||
|
||||
// EEPROM Map.
|
||||
enum AX88772B_EEPROM {
|
||||
EEPROM_772B_NODE_ID = 0x04,
|
||||
EEPROM_772B_PHY_PWRCFG = 0x18
|
||||
};
|
||||
|
||||
enum AX88772B_MFB {
|
||||
AX88772B_MFB_2K = 0,
|
||||
AX88772B_MFB_4K = 1,
|
||||
AX88772B_MFB_6K = 2,
|
||||
AX88772B_MFB_8K = 3,
|
||||
AX88772B_MFB_16K = 4,
|
||||
AX88772B_MFB_20K = 5,
|
||||
AX88772B_MFB_24K = 6,
|
||||
AX88772B_MFB_32K = 7,
|
||||
AX88772B_MFB_Count = 8
|
||||
};
|
||||
|
||||
struct _AX88772B_MFB {
|
||||
size_t ByteCount;
|
||||
size_t Threshold;
|
||||
size_t Size;
|
||||
|
||||
} AX88772B_MFBTable[AX88772B_MFB_Count] = {
|
||||
{ 0x8000, 0x8001, 2048 },
|
||||
{ 0x8100, 0x8147, 4096 },
|
||||
{ 0x8200, 0x81EB, 6144 },
|
||||
{ 0x8300, 0x83D7, 8192 },
|
||||
{ 0x8400, 0x851E, 16384 },
|
||||
{ 0x8500, 0x8666, 20480 },
|
||||
{ 0x8600, 0x87AE, 24576 },
|
||||
{ 0x8700, 0x8A3D, 32768 }
|
||||
};
|
||||
|
||||
const uint16 maxFrameSize = 1536;
|
||||
|
||||
|
||||
AX88772Device::AX88772Device(usb_device device, DeviceInfo& deviceInfo)
|
||||
:
|
||||
ASIXDevice(device, deviceInfo)
|
||||
{
|
||||
fStatus = InitDevice();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AX88772Device::InitDevice()
|
||||
{
|
||||
fFrameSize = maxFrameSize;
|
||||
fUseTRXHeader = true;
|
||||
|
||||
fReadNodeIDRequest = READ_NODEID;
|
||||
|
||||
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::ReadMACAddress(ether_address_t *address)
|
||||
{
|
||||
if (fDeviceInfo.fType != DeviceInfo::AX88772B)
|
||||
return ASIXDevice::ReadMACAddress(address);
|
||||
|
||||
// Auto-loaded default station address from internal ROM is
|
||||
// 00:00:00:00:00:00 such that an explicit access to EEPROM
|
||||
// is required to get real station address.
|
||||
for (size_t i = 0; i < sizeof(ether_address_t) / 2; i++) {
|
||||
size_t actual_length = 0;
|
||||
uint16 addr = 0;
|
||||
status_t result = gUSBModule->send_request(fDevice,
|
||||
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_SROM,
|
||||
EEPROM_772B_NODE_ID + i, 0, sizeof(addr), &addr, &actual_length);
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("Error reading MAC[%d] address:%#010x\n", i, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
address->ebyte[i * 2 + 0] = (uint8)addr;
|
||||
address->ebyte[i * 2 + 1] = (uint8)(addr >> 8);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
switch (fDeviceInfo.fType) {
|
||||
case DeviceInfo::AX88772A:
|
||||
result = _SetupAX88772A();
|
||||
break;
|
||||
case DeviceInfo::AX88772B:
|
||||
result = _SetupAX88772B();
|
||||
break;
|
||||
default:
|
||||
result = _SetupAX88772();
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
result = fMII.SetupPHY();
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t actualLength = 0;
|
||||
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::_SetupAX88772()
|
||||
{
|
||||
size_t actualLength = 0;
|
||||
// enable GPIO2 - magic from FreeBSD's if_axe
|
||||
uint16 GPIOs = GPIO_OO_2EN | GPIO_IO_2 | GPIO_RSE;
|
||||
status_t 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);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AX88772Device::_WakeupPHY()
|
||||
{
|
||||
// select PHY
|
||||
bool useEmbeddedPHY = fMII.PHYID() == PHYIDEmbedded;
|
||||
uint16 selectPHY = useEmbeddedPHY
|
||||
? SW_PHY_SEL_STATUS_INT : SW_PHY_SEL_STATUS_EXT;
|
||||
|
||||
selectPHY |= SW_PHY_SEL_STATUS_SS_MII | SW_PHY_SEL_STATUS_SS_ENB;
|
||||
|
||||
size_t actualLength = 0;
|
||||
status_t result = gUSBModule->send_request(fDevice,
|
||||
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_PHY_SEL,
|
||||
selectPHY, 0, 0, 0, &actualLength);
|
||||
snooze(31000);
|
||||
|
||||
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[] = {
|
||||
{ SW_RESET_IPRL | SW_RESET_IPPD, 250000 },
|
||||
{ SW_RESET_IPRL, 1000000 },
|
||||
{ SW_RESET_CLR, 31000 },
|
||||
{ SW_RESET_IPRL, 31000 }
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < B_COUNT_OF(resetCommands); 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;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AX88772Device::_SetupAX88772A()
|
||||
{
|
||||
// Reload EEPROM
|
||||
size_t actualLength = 0;
|
||||
status_t result = gUSBModule->send_request(fDevice,
|
||||
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_GPIOS,
|
||||
GPIO_RSE, 0, 0, 0, &actualLength);
|
||||
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("Error of reloading EEPROM: %#010x\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = _WakeupPHY();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fIPG[0] = 0x15;
|
||||
fIPG[1] = 0x16;
|
||||
fIPG[2] = 0x1A;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AX88772Device::_SetupAX88772B()
|
||||
{
|
||||
// Reload EEPROM
|
||||
size_t actualLength = 0;
|
||||
status_t result = gUSBModule->send_request(fDevice,
|
||||
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_GPIOS,
|
||||
GPIO_RSE, 0, 0, 0, &actualLength);
|
||||
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("Error of reloading EEPROM: %#010x\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = _WakeupPHY();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fIPG[0] = 0x15;
|
||||
fIPG[1] = 0x16;
|
||||
fIPG[2] = 0x1A;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
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 = 0;
|
||||
|
||||
// AX88772B uses different maximum frame burst configuration.
|
||||
if (fDeviceInfo.fType == DeviceInfo::AX88772B) {
|
||||
result = gUSBModule->send_request(fDevice,
|
||||
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_RXCONTROL_CFG,
|
||||
AX88772B_MFBTable[AX88772B_MFB_2K].ByteCount,
|
||||
AX88772B_MFBTable[AX88772B_MFB_2K].Threshold, 0, 0, &actualLength);
|
||||
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("Error of writing frame burst:%#010x\n", result);
|
||||
return result;
|
||||
}
|
||||
rxcontrol = RXCTL_HDR_TYPE_1;
|
||||
} else {
|
||||
// TODO: FreeBSD documents this to speed up xfers, I don't
|
||||
// have the hardware to test however.
|
||||
// rxcontrol = RXCTL_USB_MFB_MAX;
|
||||
}
|
||||
|
||||
rxcontrol |= RXCTL_START | RXCTL_BROADCAST;
|
||||
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)
|
||||
? 100000000 : 10000000;
|
||||
|
||||
TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n",
|
||||
(linkState->media & IFM_ACTIVE) ? "active" : "inactive",
|
||||
linkState->speed / 1000000,
|
||||
(linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
|
||||
return B_OK;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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, DeviceInfo& info);
|
||||
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);
|
||||
virtual status_t ReadMACAddress(ether_address_t *address);
|
||||
|
||||
status_t _WakeupPHY();
|
||||
status_t _SetupAX88772();
|
||||
status_t _SetupAX88772A();
|
||||
status_t _SetupAX88772B();
|
||||
};
|
||||
|
||||
#endif // _USB_AX88772_DEVICE_H_
|
||||
@@ -1,394 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 <stdio.h>
|
||||
|
||||
#include <lock.h> // for mutex
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "AX88172Device.h"
|
||||
#include "AX88178Device.h"
|
||||
#include "AX88772Device.h"
|
||||
#include "Settings.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;
|
||||
|
||||
|
||||
// IMPORTANT: keep entries sorted by ids to let the
|
||||
// binary search lookup procedure work correctly !!!
|
||||
DeviceInfo gSupportedDevices[] = {
|
||||
{ { 0x0411, 0x003d }, DeviceInfo::AX88172, "Melco LUA-U2-KTX" },
|
||||
{ { 0x0411, 0x006e }, DeviceInfo::AX88178, "Melco LUA3-U2-AGT" },
|
||||
{ { 0x04bb, 0x0930 }, DeviceInfo::AX88178, "I/O Data ETG-US2" },
|
||||
{ { 0x04f1, 0x3008 }, DeviceInfo::AX88172, "JVC MP-PRX1" },
|
||||
{ { 0x050d, 0x5055 }, DeviceInfo::AX88178, "Belkin F5D5055" },
|
||||
{ { 0x0557, 0x2009 }, DeviceInfo::AX88172, "ATEN UC-210T" },
|
||||
{ { 0x05ac, 0x1402 }, DeviceInfo::AX88772, "Apple A1277" },
|
||||
{ { 0x077b, 0x2226 }, DeviceInfo::AX88172, "LinkSys USB 2.0" },
|
||||
{ { 0x0789, 0x0160 }, DeviceInfo::AX88178, "Logitec LAN-GTJ/U2A" },
|
||||
{ { 0x07aa, 0x0017 }, DeviceInfo::AX88172, "Corega USB2TX" },
|
||||
{ { 0x07b8, 0x420a }, DeviceInfo::AX88172, "ABOCOM UF200" },
|
||||
{ { 0x07d1, 0x3c05 }, DeviceInfo::AX88772, "D-Link DUB-E100 rev.B1" },
|
||||
{ { 0x0846, 0x1040 }, DeviceInfo::AX88172, "NetGear USB 2.0 Ethernet" },
|
||||
{ { 0x086e, 0x1920 }, DeviceInfo::AX88172, "System TALKS SGC-X2UL" },
|
||||
{ { 0x08dd, 0x90ff }, DeviceInfo::AX88172, "Billionton USB2AR" },
|
||||
{ { 0x0b95, 0x1720 }, DeviceInfo::AX88172, "ASIX 88172 10/100" },
|
||||
{ { 0x0b95, 0x1780 }, DeviceInfo::AX88178, "ASIX 88178 10/100/1000" },
|
||||
{ { 0x0b95, 0x7720 }, DeviceInfo::AX88772, "ASIX 88772 10/100" },
|
||||
{ { 0x0b95, 0x772a }, DeviceInfo::AX88772A, "AX88772A 10/100" },
|
||||
{ { 0x0b95, 0x772b }, DeviceInfo::AX88772B, "AX88772B 10/100" },
|
||||
{ { 0x0b95, 0x7e2b }, DeviceInfo::AX88772B, "AX88772B 10/100" },
|
||||
{ { 0x0df6, 0x0056 }, DeviceInfo::AX88178, "Sitecom LN-031" },
|
||||
{ { 0x0df6, 0x061c }, DeviceInfo::AX88178, "Sitecom LN-028" },
|
||||
{ { 0x1189, 0x0893 }, DeviceInfo::AX88172, "Acer C&M EP-1427X-2" },
|
||||
{ { 0x13b1, 0x0018 }, DeviceInfo::AX88772A, "Linksys USB200M rev.2" },
|
||||
{ { 0x14ea, 0xab11 }, DeviceInfo::AX88178, "Planex GU-1000T" },
|
||||
{ { 0x1557, 0x7720 }, DeviceInfo::AX88772, "OQO 01+ Ethernet" },
|
||||
{ { 0x1631, 0x6200 }, DeviceInfo::AX88172, "GoodWay USB2Ethernet" },
|
||||
{ { 0x1737, 0x0039 }, DeviceInfo::AX88178, "LinkSys 1000" },
|
||||
{ { 0x17ef, 0x7203 }, DeviceInfo::AX88772, "Lenovo U2L100P 10/100" },
|
||||
{ { 0x2001, 0x1a00 }, DeviceInfo::AX88172, "D-Link DUB-E100" },
|
||||
{ { 0x2001, 0x1a02 }, DeviceInfo::AX88772B, "D-Link DUB-E100 rev.C1" },
|
||||
{ { 0x2001, 0x3c05 }, DeviceInfo::AX88772, "D-Link DUB-E100 rev.B1" },
|
||||
{ { 0x6189, 0x182d }, DeviceInfo::AX88172, "Sitecom LN-029" },
|
||||
};
|
||||
|
||||
|
||||
ASIXDevice *
|
||||
lookup_and_create_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;
|
||||
}
|
||||
|
||||
TRACE("trying %#06x:%#06x.\n",
|
||||
deviceDescriptor->vendor_id, deviceDescriptor->product_id);
|
||||
|
||||
// use binary search to lookup device in table
|
||||
uint32 id = deviceDescriptor->vendor_id << 16
|
||||
| deviceDescriptor->product_id;
|
||||
int left = -1;
|
||||
int right = B_COUNT_OF(gSupportedDevices);
|
||||
while ((right - left) > 1) {
|
||||
int i = (left + right) / 2;
|
||||
((gSupportedDevices[i].Key() < id) ? left : right) = i;
|
||||
}
|
||||
|
||||
if (gSupportedDevices[right].Key() == id) {
|
||||
switch (gSupportedDevices[right].fType) {
|
||||
case DeviceInfo::AX88172:
|
||||
return new AX88172Device(device, gSupportedDevices[right]);
|
||||
case DeviceInfo::AX88772:
|
||||
case DeviceInfo::AX88772A:
|
||||
case DeviceInfo::AX88772B:
|
||||
return new AX88772Device(device, gSupportedDevices[right]);
|
||||
case DeviceInfo::AX88178:
|
||||
return new AX88178Device(device, gSupportedDevices[right]);
|
||||
default:
|
||||
TRACE_ALWAYS("Unknown device type:%#x ignored.\n",
|
||||
static_cast<int>(gSupportedDevices[right].fType));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
TRACE_ALWAYS("Search for %#x failed %d-%d.\n", id, left, right);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
usb_asix_device_added(usb_device device, void **cookie)
|
||||
{
|
||||
*cookie = NULL;
|
||||
|
||||
MutexLocker lock(gDriverLock); // 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 = lookup_and_create_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)
|
||||
{
|
||||
MutexLocker lock(gDriverLock); // 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
|
||||
};
|
||||
|
||||
const size_t count = B_COUNT_OF(gSupportedDevices);
|
||||
static usb_support_descriptor sDescriptors[count] = {{ 0 }};
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
sDescriptors[i].vendor = gSupportedDevices[i].VendorId();
|
||||
sDescriptors[i].product = gSupportedDevices[i].ProductId();
|
||||
}
|
||||
|
||||
gUSBModule->register_driver(DRIVER_NAME, sDescriptors, count, NULL);
|
||||
gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks);
|
||||
|
||||
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)
|
||||
{
|
||||
MutexLocker lock(gDriverLock); // 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;
|
||||
|
||||
MutexLocker lock(gDriverLock); // 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;
|
||||
}
|
||||
|
||||
MutexLocker lock(gDriverLock); // 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%" B_PRId32, sDeviceBaseName,
|
||||
i);
|
||||
TRACE("publishing %s\n", gDeviceNames[deviceCount]);
|
||||
deviceCount++;
|
||||
} else
|
||||
TRACE_ALWAYS("Error: out of memory during allocating dev.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;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 <Drivers.h>
|
||||
#include <USB3.h>
|
||||
|
||||
|
||||
#define DRIVER_NAME "usb_asix"
|
||||
#define MAX_DEVICES 8
|
||||
|
||||
|
||||
const uint8 kInvalidRequest = 0xff;
|
||||
const char* const kVersion = "ver.0.10.1";
|
||||
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_
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel drivers network ether usb_asix ;
|
||||
|
||||
UsePrivateHeaders kernel net ;
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon usb_asix :
|
||||
Driver.cpp
|
||||
ASIXDevice.cpp
|
||||
AX88172Device.cpp
|
||||
AX88772Device.cpp
|
||||
AX88178Device.cpp
|
||||
Settings.cpp
|
||||
MIIBus.cpp
|
||||
;
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 "MIIBus.h"
|
||||
|
||||
#include "ASIXVendorRequests.h"
|
||||
#include "Driver.h"
|
||||
#include "Settings.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()
|
||||
:
|
||||
fStatus(B_NO_INIT),
|
||||
fDevice(0),
|
||||
fSelectedPHY(CurrentPHY)
|
||||
{
|
||||
for (size_t i = 0; i < PHYsCount; i++) {
|
||||
fPHYs[i] = PHYNotInstalled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MIIBus::Init(usb_device device)
|
||||
{
|
||||
// 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, READ_PHYID, 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; 2ndary:%#02x\n",
|
||||
PHYID(CurrentPHY), PHYID(PrimaryPHY), PHYID(SecondaryPHY));
|
||||
if (fSelectedPHY == CurrentPHY) {
|
||||
TRACE_ALWAYS("No PHYs found!\n");
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
fDevice = device;
|
||||
fStatus = result;
|
||||
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
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 /*= CurrPHY*/)
|
||||
{
|
||||
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, SW_MII_OP, 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, READ_MII, 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, HW_MII_OP, 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 /*= CurrPHY*/)
|
||||
{
|
||||
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, SW_MII_OP, 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, WRITE_MII, 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, HW_MII_OP, 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,
|
||||
SW_MII_OP, 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, READ_MII, 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, HW_MII_OP, 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;
|
||||
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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 FD
|
||||
BMSR_CAP_100BASE_TXHD = 0x2000, // PHY is able to perform 100base-TX HD
|
||||
BMSR_CAP_10BASE_TXFD = 0x1000, // PHY is able to perform 10base-TX FD
|
||||
BMSR_CAP_10BASE_TXHD = 0x0800, // PHY is able to perform 10base-TX HD
|
||||
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 a-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 ack-ed
|
||||
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 sel. bits (hardcoded to ethernet)
|
||||
};
|
||||
|
||||
|
||||
enum MII_ANLPAR {
|
||||
ANLPAR_NP = 0x8000, // Link partner next page enabled
|
||||
ANLPAR_ACK = 0x4000, // Link partner data reception ability ack-ed
|
||||
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 FD supported by link partner
|
||||
ANLPAR_TX_HD = 0x0080, // 100BASE-TX HD supported by link partner
|
||||
ANLPAR_10_FD = 0x0040, // 10BASE-TX FD supported by link partner
|
||||
ANLPAR_10_HD = 0x0020, // 10BASE-TX HD supported by link partner
|
||||
ANLPAR_SELECTOR = 0x0001 // Link partner's bin. encoded protocol selector
|
||||
};
|
||||
|
||||
|
||||
// index used to different PHY on MII bus
|
||||
enum PHYIndex {
|
||||
CurrentPHY = -1, // currently selected PHY.
|
||||
// Internally used as def. index in case no 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);
|
||||
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:
|
||||
status_t fStatus;
|
||||
usb_device fDevice;
|
||||
uint8 fPHYs[PHYsCount];
|
||||
PHYIndex fSelectedPHY;
|
||||
};
|
||||
|
||||
#endif // _USB_MII_BUS_H_
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008,2011 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 <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <driver_settings.h>
|
||||
#include <lock.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);
|
||||
int fd = open(gLogFilePath, flags, 0666);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
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 const char *prefix = "\33[33m" DRIVER_NAME ":\33[0m";
|
||||
static char buffer[1024];
|
||||
char *buf_ptr = buffer;
|
||||
if (gLogFilePath == NULL) {
|
||||
strlcpy(buffer, prefix, sizeof(buffer));
|
||||
buf_ptr += strlen(prefix);
|
||||
}
|
||||
|
||||
if (gAddTimeStamp) {
|
||||
bigtime_t time = system_time();
|
||||
uint32 msec = time / 1000;
|
||||
uint32 sec = msec / 1000;
|
||||
sprintf(buf_ptr, "%02" B_PRId32 ".%02" B_PRId32 ".%03" B_PRId32 ":",
|
||||
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("%s", buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&gLogLock);
|
||||
int fd = open(gLogFilePath, O_WRONLY | O_APPEND);
|
||||
if (fd >= 0) {
|
||||
write(fd, buffer, strlen(buffer));
|
||||
close(fd);
|
||||
}
|
||||
mutex_unlock(&gLogLock);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
|
||||
* Copyright (c) 2008, 2011 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_
|
||||
@@ -1,35 +0,0 @@
|
||||
##
|
||||
## 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
|
||||
Reference in New Issue
Block a user