From a323ccd947644bd0747b260fd74b93d90d9dc5ff Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Thu, 7 Jan 2010 22:48:20 +0000 Subject: [PATCH] Work in progress driver for davicom 9601 ethernet usb chipset. So far it reads the MAC address from the device. It is not finished yet, so don't try to use it. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34941 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/network/Jamfile | 1 + .../network/usb_davicom/ASIXDevice.cpp | 675 ++++++++++++++++++ .../drivers/network/usb_davicom/ASIXDevice.h | 111 +++ .../drivers/network/usb_davicom/Driver.cpp | 336 +++++++++ .../drivers/network/usb_davicom/Driver.h | 54 ++ .../drivers/network/usb_davicom/Jamfile | 11 + .../drivers/network/usb_davicom/Settings.cpp | 111 +++ .../drivers/network/usb_davicom/Settings.h | 34 + .../network/usb_davicom/usb_davicom.settings | 35 + 9 files changed, 1368 insertions(+) create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/ASIXDevice.cpp create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/ASIXDevice.h create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/Driver.cpp create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/Driver.h create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/Jamfile create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/Settings.cpp create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/Settings.h create mode 100644 src/add-ons/kernel/drivers/network/usb_davicom/usb_davicom.settings diff --git a/src/add-ons/kernel/drivers/network/Jamfile b/src/add-ons/kernel/drivers/network/Jamfile index 26912781b1..a7d3ef3129 100644 --- a/src/add-ons/kernel/drivers/network/Jamfile +++ b/src/add-ons/kernel/drivers/network/Jamfile @@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8139 ; SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8169 ; SubInclude HAIKU_TOP src add-ons kernel drivers network sis900 ; SubInclude HAIKU_TOP src add-ons kernel drivers network usb_asix ; +SubInclude HAIKU_TOP src add-ons kernel drivers network usb_davicom ; SubInclude HAIKU_TOP src add-ons kernel drivers network usb_ecm ; SubInclude HAIKU_TOP src add-ons kernel drivers network via_rhine ; SubInclude HAIKU_TOP src add-ons kernel drivers network vlance ; diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/ASIXDevice.cpp b/src/add-ons/kernel/drivers/network/usb_davicom/ASIXDevice.cpp new file mode 100644 index 0000000000..afc847e5f2 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/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_davicom/ASIXDevice.h b/src/add-ons/kernel/drivers/network/usb_davicom/ASIXDevice.h new file mode 100644 index 0000000000..ff3807f7cc --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/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_davicom/Driver.cpp b/src/add-ons/kernel/drivers/network/usb_davicom/Driver.cpp new file mode 100644 index 0000000000..c240a2b393 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/Driver.cpp @@ -0,0 +1,336 @@ +/* + * Davicom 9601 USB 1.1 Ethernet Driver. + * Copyright 2009 Adrien Destugues + * Distributed under the terms of the MIT license. + * + * Heavily based on code of + * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver. + * Copyright (c) 2008 S.Zharski + * Distributed under the terms of the MIT license. + * + * 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 "DavicomDevice.h" +#include "Driver.h" +#include "Settings.h" + +int32 api_version = B_CUR_DRIVER_API_VERSION; +static const char *sDeviceBaseName = "net/usb_davicom/"; +DavicomDevice *gDavicomDevices[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); } +}; + + +DavicomDevice * +create_davicom_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)) { + case IDS(0x0fe6, 0x8101): return new DavicomDevice(device, "Sunrising JP108"); + case IDS(0x07aa, 0x9601): return new DavicomDevice(device, "Corega FEther USB-TXC"); + case IDS(0x0a46, 0x9601): return new DavicomDevice(device, "Davicom USB-100"); + case IDS(0x0a46, 0x6688): return new DavicomDevice(device, "ZT6688 USB NIC"); + case IDS(0x0a46, 0x0268): return new DavicomDevice(device, "ShanTou ST268 USB NIC"); + case IDS(0x0a46, 0x8515): return new DavicomDevice(device, "ADMtek ADM8515 USB NIC"); + case IDS(0x0a47, 0x9601): return new DavicomDevice(device, "Hirose USB-100"); + case IDS(0x0a46, 0x9000): return new DavicomDevice(device, "DM9000E"); + } + return NULL; +} + + +status_t +usb_davicom_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 (gDavicomDevices[i] == NULL) + continue; + + if (gDavicomDevices[i]->CompareAndReattach(device) != B_OK) + continue; + + TRACE("The device is plugged back. Use entry at %ld.\n", i); + *cookie = gDavicomDevices[i]; + return B_OK; + } + + // no such device yet, create a new one + DavicomDevice *davicomDevice = create_davicom_device(device); + if (davicomDevice == 0) { + return ENODEV; + } + + status_t status = davicomDevice->InitCheck(); + if (status < B_OK) { + delete davicomDevice; + return status; + } + + status = davicomDevice->SetupDevice(false); + if (status < B_OK) { + delete davicomDevice; + return status; + } + + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDavicomDevices[i] != NULL) + continue; + + gDavicomDevices[i] = davicomDevice; + *cookie = davicomDevice; + + 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 davicomDevice; + return B_ERROR; +} + + +status_t +usb_davicom_device_removed(void *cookie) +{ + DriverSmartLock driverLock; // released on exit + + DavicomDevice *device = (DavicomDevice *)cookie; + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDavicomDevices[i] == device) { + if (device->IsOpen()) { + // the device will be deleted upon being freed + device->Removed(); + } else { + gDavicomDevices[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++) + gDavicomDevices[i] = NULL; + + gDeviceNames[0] = NULL; + mutex_init(&gDriverLock, DRIVER_NAME"_devices"); + + static usb_notify_hooks notifyHooks = { + &usb_davicom_device_added, + &usb_davicom_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 (gDavicomDevices[i]) { + delete gDavicomDevices[i]; + gDavicomDevices[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_davicom_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 && gDavicomDevices[index]) { + status = gDavicomDevices[index]->Open(flags); + *cookie = gDavicomDevices[index]; + } + + return status; +} + + +static status_t +usb_davicom_read(void *cookie, off_t position, void *buffer, size_t *numBytes) +{ + DavicomDevice *device = (DavicomDevice *)cookie; + return device->Read((uint8 *)buffer, numBytes); +} + + +static status_t +usb_davicom_write(void *cookie, off_t position, const void *buffer, + size_t *numBytes) +{ + DavicomDevice *device = (DavicomDevice *)cookie; + return device->Write((const uint8 *)buffer, numBytes); +} + + +static status_t +usb_davicom_control(void *cookie, uint32 op, void *buffer, size_t length) +{ + DavicomDevice *device = (DavicomDevice *)cookie; + return device->Control(op, buffer, length); +} + + +static status_t +usb_davicom_close(void *cookie) +{ + DavicomDevice *device = (DavicomDevice *)cookie; + return device->Close(); +} + + +static status_t +usb_davicom_free(void *cookie) +{ + DavicomDevice *device = (DavicomDevice *)cookie; + + DriverSmartLock driverLock; // released on exit + + status_t status = device->Free(); + for (int32 i = 0; i < MAX_DEVICES; i++) { + if (gDavicomDevices[i] == device) { + // the device is removed already but as it was open the + // removed hook has not deleted the object + gDavicomDevices[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 (gDavicomDevices[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_davicom_open, + usb_davicom_close, + usb_davicom_free, + usb_davicom_control, + usb_davicom_read, + usb_davicom_write, + NULL, /* select */ + NULL /* deselect */ + }; + + return &deviceHooks; +} + diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/Driver.h b/src/add-ons/kernel/drivers/network/usb_davicom/Driver.h new file mode 100644 index 0000000000..2a7bb3cfa1 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/Driver.h @@ -0,0 +1,54 @@ +/* + * Davicom 9601 USB 1.1 Ethernet Driver. + * Copyright 2009 Adrien Destugues + * Distributed under the terms of the MIT license. + * + * Heavily based on code of : + * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver. + * Copyright (c) 2008 S.Zharski + * Distributed under the terms of the MIT license. + * + * Driver for USB Ethernet Control Model devices + * Copyright (C) 2008 Michael Lotz + * Distributed under the terms of the MIT license. + * + */ + +#ifndef _USB_DAVICOM_DRIVER_H_ +#define _USB_DAVICOM_DRIVER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "usb_davicom" +#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_davicom_device_added(usb_device device, void **cookie); +status_t usb_davicom_device_removed(void *cookie); + +status_t init_hardware(); +void uninit_driver(); + +const char **publish_devices(); +device_hooks *find_device(const char *name); +} + + +#endif //_USB_DAVICOM_DRIVER_H_ + diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/Jamfile b/src/add-ons/kernel/drivers/network/usb_davicom/Jamfile new file mode 100644 index 0000000000..8f06287b7d --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/Jamfile @@ -0,0 +1,11 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network usb_davicom ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders kernel net ; + +KernelAddon usb_davicom : + Driver.cpp + DavicomDevice.cpp + Settings.cpp + ; diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/Settings.cpp b/src/add-ons/kernel/drivers/network/usb_davicom/Settings.cpp new file mode 100644 index 0000000000..e32d7d1e4a --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/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_davicom_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){ + 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_davicom/Settings.h b/src/add-ons/kernel/drivers/network/usb_davicom/Settings.h new file mode 100644 index 0000000000..985057d011 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/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_DAVICOM_SETTINGS_H_ + #define _USB_DAVICOM_SETTINGS_H_ + +#include + +#include "Driver.h" + +void load_settings(); +void release_settings(); + +void usb_davicom_trace(bool force, const char *func, const char *fmt, ...); + +#define TRACE(x...) usb_davicom_trace(false, __func__, x) +#define TRACE_ALWAYS(x...) usb_davicom_trace(true, __func__, x) + +extern bool gTraceFlow; +#define TRACE_FLOW(x...) usb_davicom_trace(gTraceFlow, NULL, x) + +#define TRACE_RET(result) usb_davicom_trace(false, __func__, \ + "Returns:%#010x\n", result); + +#endif /*_USB_DAVICOM_SETTINGS_H_*/ diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/usb_davicom.settings b/src/add-ons/kernel/drivers/network/usb_davicom/usb_davicom.settings new file mode 100644 index 0000000000..9f27d33820 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/usb_davicom/usb_davicom.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_davicom.log + +## reset_logfile [on|off] - truncate private log file on driver/system restart +## default value: off +## + +reset_logfile on + + +## 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