/* * Davicom DM9601 USB 1.1 Ethernet Driver. * Copyright (c) 2008, 2011 Siarzhuk Zharski * Copyright (c) 2009 Adrien Destugues * 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 "DavicomDevice.h" #include #include #include "Driver.h" #include "Settings.h" const int kFrameSize = 1522; enum VendorRequests { ReqReadRegister = 0, ReqWriteRegister = 1, ReqWriteRegisterByte = 3, }; enum DM9601Registers { RegNCR = 0x00, // Network Control Register NCRExtPHY = 0x80, // Select External PHY NCRFullDX = 0x08, // Full duplex NCRLoopback = 0x06, // Internal PHY analog loopback RegNSR = 0x01, // Network Status Register NSRSpeed10 = 0x80, // 0 = 100MBps, 1 = 10MBps (internal PHY) NSRLinkUp = 0x40, // 1 = link up (internal PHY) NSRTXFull = 0x10, // TX FIFO full NSRRXOver = 0x08, // RX FIFO overflow RegRCR = 0x05, // RX Control Register RCRDiscardLong = 0x20, // Discard long packet (over 1522 bytes) RCRDiscardCRC = 0x10, // Discard CRC error packet RCRAllMulticast = 0x08, // Pass all multicast RCRPromiscuous = 0x02, // Promiscuous RCRRXEnable = 0x01, // RX enable RegEPCR = 0x0b, // EEPROM & PHY Control Register EPCROpSelect = 0x08, // EEPROM or PHY Operation Select EPCRRegRead = 0x04, // EEPROM or PHY Register Read Command EPCRRegWrite = 0x02, // EEPROM or PHY Register Write Command RegEPAR = 0x0c, // EEPROM & PHY Address Register EPARIntPHY = 0x40, // [7:6] force to 01 if Internal PHY is selected EPARMask = 0x1f, // mask [0:5] RegEPDRL = 0x0d, // EEPROM & PHY Low Byte Data Register RegEPDRH = 0x0e, // EEPROM & PHY Low Byte Data Register RegPAR = 0x10, // [0x10 - 0x15] Physical Address Register RegMAR = 0x16, // [0x16 - 0x1d] Multicast Address Register RegGPCR = 0x1E, // General Purpose Control Register GPCRPowerDown = 0x01, // [0:6] Define in/out direction of GPCR // GPIO0 - is output for Power Down function RegGPR = 0x1F, // General Purpose Register GPRPowerDownInPHY = 0x01, // Power down Internal PHY RegUSBC = 0xf4, // USB Control Register USBCIntAck = 0x20, // ACK with 8-bytes of data on interrupt EP USBCIntNAck = 0x10, // Supress ACK on interrupt EP }; enum MIIRegisters { RegBMCR = 0x00, BMCRIsolate = 0x0400, BMCRReset = 0x8000, RegBMSR = 0x01, RegPHYID1 = 0x02, RegPHYID2 = 0x03, }; #define MII_OUI(id1, id2) (((id1) << 6) | ((id2) >> 10)) #define MII_MODEL(id2) (((id2) & 0x03f0) >> 4) #define MII_REV(id2) ((id2) & 0x000f) DavicomDevice::DavicomDevice(usb_device device, DeviceInfo& deviceInfo) : fDevice(device), fStatus(B_ERROR), fOpen(false), fRemoved(false), fHasConnection(false), fTXBufferFull(false), fNonBlocking(false), fInsideNotify(0), fNotifyEndpoint(0), fReadEndpoint(0), fWriteEndpoint(0), fMaxTXPacketSize(0), fActualLengthRead(0), fActualLengthWrite(0), fStatusRead(0), fStatusWrite(0), fNotifyReadSem(-1), fNotifyWriteSem(-1), fLinkStateChangeSem(-1), fNotifyData(NULL) { fDeviceInfo = deviceInfo; memset(&fMACAddress, 0, sizeof(fMACAddress)); fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read"); if (fNotifyReadSem < B_OK) { TRACE_ALWAYS("Error of creating read notify semaphore:%#010x\n", fNotifyReadSem); return; } fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write"); if (fNotifyWriteSem < B_OK) { TRACE_ALWAYS("Error of creating write notify semaphore:%#010x\n", fNotifyWriteSem); return; } fNotifyData = new DM9601NotifyData(); if (fNotifyData == NULL) { TRACE_ALWAYS("Error allocating notify buffer\n"); return; } if (_SetupEndpoints() != B_OK) { return; } _InitMII(); fStatus = B_OK; TRACE("Created!\n"); } DavicomDevice::~DavicomDevice() { if (fNotifyReadSem >= B_OK) delete_sem(fNotifyReadSem); if (fNotifyWriteSem >= B_OK) delete_sem(fNotifyWriteSem); if (!fRemoved) // ??? gUSBModule->cancel_queued_transfers(fNotifyEndpoint); delete fNotifyData; TRACE("Deleted!\n"); } status_t DavicomDevice::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, fNotifyData, sizeof(DM9601NotifyData), _NotifyCallback, this); if (result != B_OK) { TRACE_ALWAYS("Error of requesting notify interrupt:%#010x\n", result); return result; } result = _EnableInterrupts(true); fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK; fOpen = true; TRACE("Opened: %#010x!\n", result); return result; } status_t DavicomDevice::Close() { if (fRemoved) { fOpen = false; return B_OK; } _EnableInterrupts(false); // 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; status_t result = _StopDevice(); TRACE("Closed: %#010x!\n", result); return result; } status_t DavicomDevice::Free() { TRACE("Freed!\n"); return B_OK; } status_t DavicomDevice::Read(uint8 *buffer, size_t *numBytes) { size_t numBytesToRead = *numBytes; *numBytes = 0; if (fRemoved) { TRACE_ALWAYS("Error of receiving %d bytes from removed device.\n", numBytesToRead); return B_DEVICE_NOT_FOUND; } TRACE_RX("Request %d bytes.\n", numBytesToRead); struct _RXHeader { uint FOE :1; uint CE :1; uint LE :1; uint PLE :1; uint RWTO:1; uint LCS :1; uint MF :1; uint RF :1; uint countLow :8; uint countHigh :8; uint8 Errors() { return 0xbf & *(uint8*)this; } } __attribute__((__packed__)); _RXHeader header = { 0 }; iovec rxData[] = { { &header, sizeof(header) }, { buffer, numBytesToRead } }; status_t result = gUSBModule->queue_bulk_v(fReadEndpoint, rxData, 2, _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); return fStatusRead; } if (fActualLengthRead < sizeof(_RXHeader)) { TRACE_ALWAYS("Error: no place for RXHeader: only %d of %d bytes.\n", fActualLengthRead, sizeof(_RXHeader)); return B_ERROR; } if (header.Errors() != 0) { TRACE_ALWAYS("RX header errors %#04x detected!\n", header.Errors()); } TRACE_STATS("FOE:%d CE:%d LE:%d PLE:%d rwTO:%d LCS:%d MF:%d RF:%d\n", header.FOE, header.CE, header.LE, header.PLE, header.RWTO, header.LCS, header.MF, header.RF); *numBytes = header.countLow | ( header.countHigh << 8 ); if (fActualLengthRead - sizeof(_RXHeader) > *numBytes) { TRACE_ALWAYS("MISMATCH of the frame length: hdr %d; received:%d\n", *numBytes, fActualLengthRead - sizeof(_RXHeader)); } TRACE_RX("Read %d bytes.\n", *numBytes); return B_OK; } status_t DavicomDevice::Write(const uint8 *buffer, size_t *numBytes) { size_t numBytesToWrite = *numBytes; *numBytes = 0; if (fRemoved) { TRACE_ALWAYS("Error of writing %d bytes to removed device.\n", numBytesToWrite); return B_DEVICE_NOT_FOUND; } if (!fHasConnection) { TRACE_ALWAYS("Error of writing %d bytes to device while down.\n", numBytesToWrite); return B_ERROR; } if (fTXBufferFull) { TRACE_ALWAYS("Error of writing %d bytes to device: TX buffer full.\n", numBytesToWrite); return B_ERROR; } TRACE_TX("Write %d bytes.\n", numBytesToWrite); // additional padding byte must be transmitted in case data size // to be send is multiple of pipe's max packet size uint16 length = numBytesToWrite; size_t count = 2; if (((numBytesToWrite + 2) % fMaxTXPacketSize) == 0) { length++; count++; } struct _TXHeader { uint8 countLow; uint8 countHigh; } __attribute__((__packed__)); _TXHeader header = { (uint8)(length & 0xff), (uint8)((length >> 8) & 0xff) }; uint8 padding = 0; iovec txData[] = { { &header, sizeof(_TXHeader) }, { (uint8*)buffer, numBytesToWrite }, { &padding, 1 } }; status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint, txData, count, _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); return fStatusWrite; } *numBytes = fActualLengthWrite - sizeof(_TXHeader); TRACE_TX("Written %d bytes.\n", *numBytes); return B_OK; } status_t DavicomDevice::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 = kFrameSize; return B_OK; case ETHER_NONBLOCK: TRACE("ETHER_NONBLOCK\n"); fNonBlocking = *((uint8*)buffer); return B_OK; case ETHER_SETPROMISC: TRACE("ETHER_SETPROMISC\n"); return _SetPromiscuousMode(*((uint8*)buffer)); case ETHER_ADDMULTI: TRACE("ETHER_ADDMULTI\n"); return _ModifyMulticastTable(true, (ether_address_t*)buffer); case ETHER_REMMULTI: TRACE("ETHER_REMMULTI\n"); return _ModifyMulticastTable(false, (ether_address_t*)buffer); case ETHER_SET_LINK_STATE_SEM: fLinkStateChangeSem = *(sem_id *)buffer; return B_OK; case ETHER_GET_LINK_STATE: return _GetLinkState((ether_link_state *)buffer); default: TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op); } return B_DEV_INVALID_IOCTL; } void DavicomDevice::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 DavicomDevice::SetupDevice(bool deviceReplugged) { ether_address address; status_t result = _ReadMACAddress(&address); if (result != B_OK) { TRACE_ALWAYS("Error 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 DavicomDevice::CompareAndReattach(usb_device device) { const usb_device_descriptor *deviceDescriptor = gUSBModule->get_device_descriptor(device); if (deviceDescriptor == NULL) { TRACE_ALWAYS("Error getting USB device descriptor.\n"); return B_ERROR; } if (deviceDescriptor->vendor_id != fDeviceInfo.VendorId() && deviceDescriptor->product_id != fDeviceInfo.ProductId()) { // this certainly isn't the same device return B_BAD_VALUE; } // this is the same device that was replugged - clear the removed state, // re-setup the endpoints and transfers and open the device if it was // previously opened fDevice = device; fRemoved = false; status_t result = _SetupEndpoints(); if (result != B_OK) { fRemoved = true; return result; } // we need to setup hardware on device replug result = SetupDevice(true); if (result != B_OK) { return result; } if (fOpen) { fOpen = false; result = Open(fNonBlocking ? O_NONBLOCK : 0); } return result; } status_t DavicomDevice::_SetupEndpoints() { const usb_configuration_info *config = gUSBModule->get_nth_configuration(fDevice, 0); if (config == NULL) { TRACE_ALWAYS("Error of getting USB device configuration.\n"); return B_ERROR; } if (config->interface_count <= 0) { TRACE_ALWAYS("Error:no interfaces found in USB device configuration\n"); return B_ERROR; } usb_interface_info *interface = config->interface[0].active; if (interface == 0) { TRACE_ALWAYS("Error:invalid active interface in " "USB device configuration\n"); return B_ERROR; } int notifyEndpoint = -1; int readEndpoint = -1; int writeEndpoint = -1; for (size_t ep = 0; ep < interface->endpoint_count; ep++) { usb_endpoint_descriptor *epd = interface->endpoint[ep].descr; if ((epd->attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_INTERRUPT) { notifyEndpoint = ep; continue; } if ((epd->attributes & USB_ENDPOINT_ATTR_MASK) != USB_ENDPOINT_ATTR_BULK) { TRACE_ALWAYS("Error: USB endpoint type %#04x is unknown.\n", epd->attributes); continue; } if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK) == USB_ENDPOINT_ADDR_DIR_IN) { readEndpoint = ep; continue; } if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK) == USB_ENDPOINT_ADDR_DIR_OUT) { writeEndpoint = ep; continue; } } if (notifyEndpoint == -1 || readEndpoint == -1 || writeEndpoint == -1) { TRACE_ALWAYS("Error: not all USB endpoints were found: notify:%d; " "read:%d; write:%d\n", notifyEndpoint, readEndpoint, writeEndpoint); return B_ERROR; } gUSBModule->set_configuration(fDevice, config); fNotifyEndpoint = interface->endpoint[notifyEndpoint].handle; fReadEndpoint = interface->endpoint[readEndpoint ].handle; fWriteEndpoint = interface->endpoint[writeEndpoint ].handle; fMaxTXPacketSize = interface->endpoint[writeEndpoint].descr->max_packet_size; return B_OK; } status_t DavicomDevice::_ReadMACAddress(ether_address_t *address) { status_t result = _ReadRegister(RegPAR, sizeof(ether_address), (uint8*)address); if (result != B_OK) { TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result); return result; } return B_OK; } status_t DavicomDevice::_StartDevice() { uint8 control = 0; // disable loopback status_t result = _ReadRegister(RegNCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading NCR: %#010x.\n", result); return result; } if (control & NCRExtPHY) TRACE_ALWAYS("Device uses external PHY\n"); control &= ~NCRLoopback; result = _Write1Register(RegNCR, control); if (result != B_OK) { TRACE_ALWAYS("Error writing %#02X to NCR: %#010x.\n", control, result); return result; } // Initialize RX control register, enable RX and activate multicast result = _ReadRegister(RegRCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading RCR: %#010x.\n", result); return result; } control &= ~RCRPromiscuous; control |= RCRDiscardLong | RCRDiscardCRC | RCRRXEnable | RCRAllMulticast; result = _Write1Register(RegRCR, control); if (result != B_OK) { TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result); return result; } // clear POWER_DOWN state of internal PHY result = _ReadRegister(RegGPCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading GPCR: %#010x.\n", result); return result; } control |= GPCRPowerDown; result = _Write1Register(RegGPCR, control); if (result != B_OK) { TRACE_ALWAYS("Error writing %#02X to GPCR: %#010x.\n", control, result); return result; } result = _ReadRegister(RegGPR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading GPR: %#010x.\n", result); return result; } control &= ~GPRPowerDownInPHY; result = _Write1Register(RegGPR, control); if (result != B_OK) { TRACE_ALWAYS("Error writing %#02X to GPR: %#010x.\n", control, result); return result; } return B_OK; } status_t DavicomDevice::_StopDevice() { uint8 control = 0; // disable RX status_t result = _ReadRegister(RegRCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading RCR: %#010x.\n", result); return result; } control &= ~RCRRXEnable; result = _Write1Register(RegRCR, control); if (result != B_OK) TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result); return result; } status_t DavicomDevice::_SetPromiscuousMode(bool on) { uint8 control = 0; status_t result = _ReadRegister(RegRCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading RCR: %#010x.\n", result); return result; } if (on) control |= RCRPromiscuous; else control &= ~RCRPromiscuous; result = _Write1Register(RegRCR, control); if (result != B_OK) TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result); return result; } uint32 DavicomDevice::_EthernetCRC32(const uint8* buffer, size_t length) { uint32 result = 0xffffffff; for (size_t i = 0; i < length; i++) { uint8 data = *buffer++; for (int bit = 0; bit < 8; bit++, data >>= 1) { uint32 carry = ((result & 0x80000000) ? 1 : 0) ^ (data & 0x01); result <<= 1; if (carry != 0) result = (result ^ 0x04c11db6) | carry; } } return result; } status_t DavicomDevice::_ModifyMulticastTable(bool join, ether_address_t *group) { char groupName[6 * 3 + 1] = { 0 }; sprintf(groupName, "%02x:%02x:%02x:%02x:%02x:%02x", group->ebyte[0], group->ebyte[1], group->ebyte[2], group->ebyte[3], group->ebyte[4], group->ebyte[5]); TRACE("%s multicast group %s\n", join ? "Joining" : "Leaving", groupName); uint32 hash = _EthernetCRC32(group->ebyte, 6); bool isInTable = fMulticastHashes.Find(hash) != fMulticastHashes.End(); if (isInTable && join) return B_OK; // already listed - nothing to do if (!isInTable && !join) { TRACE_ALWAYS("Cannot leave unlisted multicast group %s!\n", groupName); return B_ERROR; } const size_t hashLength = 8; uint8 hashTable[hashLength] = { 0 }; hashTable[hashLength - 1] |= 0x80; // broadcast address status_t result = _WriteRegister(RegMAR, hashLength, hashTable); if (result != B_OK) { TRACE_ALWAYS("Error initializing MAR: %#010x.\n", result); return result; } if (join) fMulticastHashes.PushBack(hash); else fMulticastHashes.Remove(hash); for (int32 i = 0; i < fMulticastHashes.Count(); i++) { uint32 hash = fMulticastHashes[i] >> 26; hashTable[hash / 8] |= 1 << (hash % 8); } // clear/set pass all multicast bit as required uint8 control = 0; result = _ReadRegister(RegRCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading RCR: %#010x.\n", result); return result; } if (fMulticastHashes.Count() > 0) control &= ~RCRAllMulticast; else control |= RCRAllMulticast; result = _Write1Register(RegRCR, control); if (result != B_OK) { TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result); return result; } result = _WriteRegister(RegMAR, hashLength, hashTable); if (result != B_OK) TRACE_ALWAYS("Error writing hash table in MAR: %#010x.\n", result); return result; } void DavicomDevice::_ReadCallback(void *cookie, int32 status, void *data, size_t actualLength) { TRACE_RX("ReadCB: %d bytes; status:%#010x\n", actualLength, status); DavicomDevice *device = (DavicomDevice *)cookie; device->fActualLengthRead = actualLength; device->fStatusRead = status; device->fStats.readCount++; release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE); } void DavicomDevice::_WriteCallback(void *cookie, int32 status, void *data, size_t actualLength) { TRACE_TX("WriteCB: %d bytes; status:%#010x\n", actualLength, status); DavicomDevice *device = (DavicomDevice *)cookie; device->fActualLengthWrite = actualLength; device->fStatusWrite = status; device->fStats.writeCount++; release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE); } void DavicomDevice::_NotifyCallback(void *cookie, int32 status, void *data, size_t actualLength) { DavicomDevice *device = (DavicomDevice *)cookie; atomic_add(&device->fInsideNotify, 1); if (status == B_CANCELED || device->fRemoved) { atomic_add(&device->fInsideNotify, -1); return; } if (status == B_OK) device->_OnNotify(actualLength); else TRACE_ALWAYS("Status error:%#010x; length:%d\n", status, actualLength); // schedule next notification buffer gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyData, sizeof(DM9601NotifyData), _NotifyCallback, device); atomic_add(&device->fInsideNotify, -1); } status_t DavicomDevice::_OnNotify(uint32 actualLength) { if (actualLength != sizeof(DM9601NotifyData)) { TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n", actualLength, sizeof(DM9601NotifyData)); return B_BAD_DATA; } bool linkIsUp = fNotifyData->LINKST != 0; fTXBufferFull = fNotifyData->TXFULL != 0; bool rxOverflow = fNotifyData->RXOV != 0; bool linkStateChange = (linkIsUp != fHasConnection); fHasConnection = linkIsUp; if (linkStateChange) { if (fHasConnection) { TRACE("Link is now up at %s Mb/s\n", fNotifyData->SPEED ? "10" : "100"); } else TRACE("Link is now down"); } #ifdef UDAV_TRACE if (gTraceStats) { if (fNotifyData->TXFULL) fStats.txFull++; if (fNotifyData->RXOV) fStats.rxOverflow++; if (fNotifyData->ROC) fStats.rxOvCount += fNotifyData->ROC; if (fNotifyData->RT) fStats.runtFrames++; if (fNotifyData->LCS) fStats.lateRXCollisions++; if (fNotifyData->RWTO) fStats.rwTOs++; if (fNotifyData->PLE) fStats.physLayerErros++; if (fNotifyData->AE) fStats.alignmentErros++; if (fNotifyData->CE) fStats.crcErrors++; if (fNotifyData->FOE) fStats.overErrors++; if (fNotifyData->TSR1.LC) fStats.lateTXCollisions++; if (fNotifyData->TSR1.LCR) fStats.lostOfCarrier++; if (fNotifyData->TSR1.NC) fStats.noCarrier++; if (fNotifyData->TSR1.COL) fStats.txCollisions++; if (fNotifyData->TSR1.EC) fStats.excCollisions++; if (fNotifyData->TSR2.LC) fStats.lateTXCollisions++; if (fNotifyData->TSR2.LCR) fStats.lostOfCarrier++; if (fNotifyData->TSR2.NC) fStats.noCarrier++; if (fNotifyData->TSR2.COL) fStats.txCollisions++; if (fNotifyData->TSR2.EC) fStats.excCollisions++; fStats.notifyCount++; } #endif if (rxOverflow) TRACE("RX buffer overflow. %d packets dropped\n", fNotifyData->ROC); uint8 tsr = 0xfc & *(uint8*)&fNotifyData->TSR1; if (tsr != 0) TRACE("TX packet 1: Status %#04x is not OK.\n", tsr); tsr = 0xfc & *(uint8*)&fNotifyData->TSR2; if (tsr != 0) TRACE("TX packet 2: Status %#04x is not OK.\n", tsr); if (linkStateChange && fLinkStateChangeSem >= B_OK) release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); return B_OK; } status_t DavicomDevice::_GetLinkState(ether_link_state *linkState) { uint8 registerValue = 0; status_t result = _ReadRegister(RegNSR, 1, ®isterValue); if (result != B_OK) { TRACE_ALWAYS("Error reading NSR register! %x\n", result); return result; } if (registerValue & NSRSpeed10) linkState->speed = 10000000; else linkState->speed = 100000000; linkState->quality = 1000; linkState->media = IFM_ETHER | IFM_100_TX; if (fHasConnection) { linkState->media |= IFM_ACTIVE; result = _ReadRegister(RegNCR, 1, ®isterValue); if (result != B_OK) { TRACE_ALWAYS("Error reading NCR register! %x\n", result); return result; } if (registerValue & NCRFullDX) linkState->media |= IFM_FULL_DUPLEX; else linkState->media |= IFM_HALF_DUPLEX; if (registerValue & NCRLoopback) linkState->media |= IFM_LOOP; } TRACE_STATE("Medium state: %s, %lld MBit/s, %s duplex.\n", (linkState->media & IFM_ACTIVE) ? "active" : "inactive", linkState->speed / 1000000, (linkState->media & IFM_FULL_DUPLEX) ? "full" : "half"); TRACE_STATS("tx:%d rx:%d rxCn:%d rtF:%d lRxC:%d rwTO:%d PLE:%d AE:%d CE:%d " "oE:%d ltxC:%d lCR:%d nC:%d txC:%d exC:%d r:%d w:%d n:%d\n", fStats.txFull, fStats.rxOverflow, fStats.rxOvCount, fStats.runtFrames, fStats.lateRXCollisions, fStats.rwTOs, fStats.physLayerErros, fStats.alignmentErros, fStats.crcErrors, fStats.overErrors, fStats.lateTXCollisions, fStats.lostOfCarrier, fStats.noCarrier, fStats.txCollisions, fStats.excCollisions, fStats.readCount, fStats.writeCount, fStats.notifyCount); return B_OK; } status_t DavicomDevice::_ReadRegister(uint8 reg, size_t size, uint8* buffer) { if (size > 255) return B_BAD_VALUE; size_t actualLength = 0; status_t result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, ReqReadRegister, 0, reg, size, buffer, &actualLength); if (size != actualLength) { TRACE_ALWAYS("Size mismatch reading register ! asked %d got %d", size, actualLength); } return result; } status_t DavicomDevice::_WriteRegister(uint8 reg, size_t size, uint8* buffer) { if (size > 255) return B_BAD_VALUE; size_t actualLength = 0; status_t result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, ReqWriteRegister, 0, reg, size, buffer, &actualLength); return result; } status_t DavicomDevice::_Write1Register(uint8 reg, uint8 value) { size_t actualLength = 0; status_t result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, ReqWriteRegisterByte, value, reg, 0, NULL, &actualLength); return result; } status_t DavicomDevice::_ReadMII(uint8 reg, uint16* data) { // select PHY and set PHY register address status_t result = _Write1Register(RegEPAR, EPARIntPHY | (reg & EPARMask)); if (result != B_OK) { TRACE_ALWAYS("Failed to set MII address %#x. Error:%#x\n", reg, result); return result; } // select PHY operation and initiate reading result = _Write1Register(RegEPCR, EPCROpSelect | EPCRRegRead); if (result != B_OK) { TRACE_ALWAYS("Failed to starting MII reading. Error:%#x\n", result); return result; } // finalize writing uint8 control = 0; result = _ReadRegister(RegEPCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Failed to read EPCR register. Error:%#x\n", result); return result; } result = _Write1Register(RegEPCR, control & ~EPCRRegRead); if (result != B_OK) { TRACE_ALWAYS("Failed to write EPCR register. Error:%#x\n", result); return result; } // retrieve the result from data registers uint8 values[2] = { 0 }; result = _ReadRegister(RegEPDRL, 2, values); if (result != B_OK) { TRACE_ALWAYS("Failed to retrieve data %#x. Error:%#x\n", data, result); return result; } *data = values[0] | values[1] << 8; return result; } status_t DavicomDevice::_WriteMII(uint8 reg, uint16 data) { // select PHY and set PHY register address status_t result = _Write1Register(RegEPAR, EPARIntPHY | (reg & EPARMask)); if (result != B_OK) { TRACE_ALWAYS("Failed to set MII address %#x. Error:%#x\n", reg, result); return result; } // put the value to data register uint8 values[] = { (uint8)(data & 0xff), (uint8)((data >> 8) & 0xff) }; result = _WriteRegister(RegEPDRL, sizeof(uint16), values); if (result != B_OK) { TRACE_ALWAYS("Failed to put data %#x. Error:%#x\n", data, result); return result; } // select PHY operation and initiate writing result = _Write1Register(RegEPCR, EPCROpSelect | EPCRRegWrite); if (result != B_OK) { TRACE_ALWAYS("Failed to starting MII wrintig. Error:%#x\n", result); return result; } // finalize writing uint8 control = 0; result = _ReadRegister(RegEPCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Failed to read EPCR register. Error:%#x\n", result); return result; } result = _Write1Register(RegEPCR, control & ~EPCRRegWrite); if (result != B_OK) TRACE_ALWAYS("Failed to write EPCR register. Error:%#x\n", result); return result; } status_t DavicomDevice::_InitMII() { uint16 control = 0; status_t result = _ReadMII(RegBMCR, &control); if (result != B_OK) { TRACE_ALWAYS("Failed to read MII BMCR register. Error:%#x\n", result); return result; } result = _WriteMII(RegBMCR, control & ~BMCRIsolate); if (result != B_OK) { TRACE_ALWAYS("Failed to clear isolate PHY. Error:%#x\n", result); return result; } result = _WriteMII(0, BMCRReset); if (result != B_OK) { TRACE_ALWAYS("Failed to reset BMCR register. Error:%#x\n", result); return result; } uint16 id01 = 0, id02 = 0; result = _ReadMII(RegPHYID1, &id01); if (result != B_OK) { TRACE_ALWAYS("Failed to read PHY ID 0. Error:%#x\n", result); return result; } result = _ReadMII(RegPHYID2, &id02); if (result != B_OK) { TRACE_ALWAYS("Failed to read PHY ID 1. Error:%#x\n", result); return result; } TRACE_ALWAYS("MII Info: OUI:%04x; Model:%04x; rev:%02x.\n", MII_OUI(id01, id02), MII_MODEL(id02), MII_REV(id02)); return result; } status_t DavicomDevice::_EnableInterrupts(bool enable) { uint8 control = 0; status_t result = _ReadRegister(RegUSBC, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error of reading USB control register:%#010x\n", result); return result; } if (enable) { control |= USBCIntAck; control &= ~USBCIntNAck; } else { control &= ~USBCIntAck; } result = _Write1Register(RegUSBC, control); if (result != B_OK) TRACE_ALWAYS("Error of setting USB control register:%#010x\n", result); return result; }