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