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