From b337f35c4582d93d7560ef105bba95b1e092807e Mon Sep 17 00:00:00 2001 From: Siarzhuk Zharski Date: Mon, 12 Sep 2011 19:35:28 +0000 Subject: [PATCH] Bringing usb_davicom to life: part 2 of 3: * This part is just fixes to let the driver work. I have decided to separate this changes just for Adrien's information because final version will be refactored significantly. * The fixes: - Endpoint 3 acknowledgements must be enabled in USB Control Register to let async notifications work; - Functions to handle MII registers and MII initialization routines are implemented and used; - Sending WRITE1_REGISTER request typo was fixed; - Small typo in queueing RX iovec fixed: we are receiving 2 blocks not 1; - one byte size padding is required in case TX packet length is multiple of pipe max packet size; - StopDevice procedure implemented; - StartDevice procedure fixed; * Code style fixes are coming soon. Please be patient. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42748 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../network/usb_davicom/DavicomDevice.cpp | 302 ++++++++++++++---- .../network/usb_davicom/DavicomDevice.h | 19 +- .../drivers/network/usb_davicom/Settings.cpp | 8 +- .../drivers/network/usb_davicom/Settings.h | 10 +- .../network/usb_davicom/usb_davicom.settings | 18 +- 5 files changed, 288 insertions(+), 69 deletions(-) diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.cpp b/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.cpp index e5a3823a93..dcb2ed13a9 100644 --- a/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.cpp +++ b/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.cpp @@ -1,22 +1,21 @@ /* * Davicom DM9601 USB 1.1 Ethernet Driver. * Copyright (c) 2009 Adrien Destugues + * Copyright (c) 2008, 2011 Siarzhuk Zharski * 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. - * + * 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 "Driver.h" #include "Settings.h" -#include "DavicomDevice.h" // Vendor commands @@ -55,6 +54,21 @@ #define GPR_GEP_GEPIO0 0x01 // Power down +#define EPCR 0x0b // EEPROM/PHY Control Register +#define EPCR_EPOS 0x08 // EEPROM/PHY Operation Select +#define EPCR_ERPRR 0x04 // EEPROM/PHY Register Read Command +#define EPCR_ERPRW 0x02 // EEPROM/PHY Register Write Command + +#define EPAR 0x0c // EEPROM/PHY Control Register +#define EPAR_ADDR0 0x40 // EEPROM/PHY Address +#define EPAR_MASK 0x1f // mask [0:5] + +#define EPDRL 0x0d // EEPROM/PHY Data Register + +#define USBCR 0xf4 // USB Control Register +#define EP3ACK 0x20 // ACK with 8-byte data on interrupt EP +#define EP3NACK 0x10 // Supress ACK on interrupt EP + //TODO: multicast support //TODO: set media state support @@ -93,7 +107,163 @@ DavicomDevice::_Write1Register(uint8 reg, uint8 value) size_t actualLength; status_t result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, - WRITE1_REGISTER, 0, reg, 1, &value, &actualLength); + WRITE1_REGISTER, 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(EPAR, EPAR_ADDR0 | (reg & EPAR_MASK)); + 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(EPCR, EPCR_EPOS | EPCR_ERPRR); + if (result != B_OK) { + TRACE_ALWAYS("Failed to starting MII reading. Error:%#x\n", result); + return result; + } + + // finalize writing + uint8 control = 0; + result = _ReadRegister(EPCR, 1, &control); + if (result != B_OK) { + TRACE_ALWAYS("Failed to read EPCR register. Error:%#x\n", result); + return result; + } + + result = _Write1Register(EPCR, control & ~EPCR_ERPRR); + 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(EPDRL, 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(EPAR, EPAR_ADDR0 | (reg & EPAR_MASK)); + 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[] = { data & 0xff, ( data >> 8 ) & 0xff }; + result = _WriteRegister(EPDRL, 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(EPCR, EPCR_EPOS | EPCR_ERPRW); + if (result != B_OK) { + TRACE_ALWAYS("Failed to starting MII wrintig. Error:%#x\n", result); + return result; + } + + // finalize writing + uint8 control = 0; + result = _ReadRegister(EPCR, 1, &control); + if (result != B_OK) { + TRACE_ALWAYS("Failed to read EPCR register. Error:%#x\n", result); + return result; + } + + result = _Write1Register(EPCR, control & ~EPCR_ERPRW); + 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(0, &control); // read BMCR + if (result != B_OK) { + TRACE_ALWAYS("Failed to read BMCR register. Error:%#x\n", result); + return result; + } + + result = _WriteMII(0, control & ~0x0400); // clear Isolate flag + if (result != B_OK) { + TRACE_ALWAYS("Failed to write BMCR register. Error:%#x\n", result); + return result; + } + + result = _WriteMII(0, 0x8000); // write reset to BMCR + if (result != B_OK) { + TRACE_ALWAYS("Failed to reset BMCR register. Error:%#x\n", result); + return result; + } + + uint16 id01 = 0, id02 = 0; + result = _ReadMII(0x02, &id01); // read PHY_ID 0 + if (result != B_OK) { + TRACE_ALWAYS("Failed to read PHY ID 0. Error:%#x\n", result); + return result; + } + + result = _ReadMII(0x03, &id02); // read PHY_ID 1 + if (result != B_OK) { + TRACE_ALWAYS("Failed to read PHY ID 1. Error:%#x\n", result); + return result; + } + +#define MII_OUI(id1, id2) (((id1) << 6) | ((id2) >> 10)) +#define MII_MODEL(id2) (((id2) & 0x03f0) >> 4) +#define MII_REV(id2) ((id2) & 0x000f) + + 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(USBCR, 1, &control); + if(result != B_OK) { + TRACE_ALWAYS("Error of reading USB control register:%#010x\n", result); + return result; + } + + if (enable) { + control |= EP3ACK; + control &= ~EP3NACK; + } else { + control &= ~EP3ACK; + } + + result = _Write1Register(USBCR, control); + if(result != B_OK) + TRACE_ALWAYS("Error of setting USB control register:%#010x\n", result); + return result; } @@ -109,6 +279,7 @@ DavicomDevice::DavicomDevice(usb_device device, const char *description) fNotifyEndpoint(0), fReadEndpoint(0), fWriteEndpoint(0), + fMaxTXPacketSize(0), fNotifyReadSem(-1), fNotifyWriteSem(-1), fNotifyBuffer(NULL), @@ -150,7 +321,7 @@ DavicomDevice::DavicomDevice(usb_device device, const char *description) return; } - // TODO : others inits here ? + _InitMII(); fStatus = B_OK; } @@ -192,6 +363,8 @@ DavicomDevice::Open(uint32 flags) return result; } + result = _EnableInterrupts(true); + fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK; fOpen = true; return result; @@ -205,6 +378,8 @@ DavicomDevice::Close() fOpen = false; return B_OK; } + + _EnableInterrupts(false); // wait until possible notification handling finished... while (atomic_add(&fInsideNotify, 0) != 0) @@ -238,16 +413,16 @@ DavicomDevice::Read(uint8 *buffer, size_t *numBytes) return B_DEVICE_NOT_FOUND; } - TRACE_FLOW("Request %d bytes.\n", numBytesToRead); +// TRACE_RX("Request %d bytes.\n", numBytesToRead); uint8 header[kRXHeaderSize]; iovec rxData[] = { - { &header, kRXHeaderSize }, + { header, kRXHeaderSize }, { buffer, numBytesToRead } }; status_t result = gUSBModule->queue_bulk_v(fReadEndpoint, - rxData, 1, _ReadCallback, this); + rxData, 2, _ReadCallback, this); if (result != B_OK) { TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result); return result; @@ -291,7 +466,7 @@ DavicomDevice::Read(uint8 *buffer, size_t *numBytes) *numBytes, fActualLengthRead - kRXHeaderSize); } - TRACE_FLOW("Read %d bytes.\n", *numBytes); +// TRACE_RX("Read %d bytes.\n", *numBytes); return B_OK; } @@ -320,19 +495,26 @@ DavicomDevice::Write(const uint8 *buffer, size_t *numBytes) return B_ERROR; } - TRACE_FLOW("Write %d bytes.\n", numBytesToWrite); + TRACE_TX("Write %d bytes.\n", numBytesToWrite); - uint8 header[kTXHeaderSize]; - header[0] = *numBytes & 0xFF; - header[1] = *numBytes >> 8; + uint16 length = numBytesToWrite; + size_t count = 2; + if (((numBytesToWrite + 2) % fMaxTXPacketSize) == 0) { + length++; + count++; + } + + uint8 header[kTXHeaderSize] = { length & 0xFF, length >> 8 }; + uint8 padding = 0; iovec txData[] = { - { &header, kTXHeaderSize }, - { (uint8*)buffer, numBytesToWrite } + { header, kTXHeaderSize }, + { (uint8*)buffer, numBytesToWrite }, + { &padding, 1 } }; status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint, - txData, 2, _WriteCallback, this); + txData, count/*2*/, _WriteCallback, this); if (result != B_OK) { TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result); return result; @@ -352,7 +534,7 @@ DavicomDevice::Write(const uint8 *buffer, size_t *numBytes) *numBytes = fActualLengthWrite - kTXHeaderSize;; - TRACE_FLOW("Written %d bytes.\n", *numBytes); + TRACE_TX("Written %d bytes.\n", *numBytes); return B_OK; } @@ -568,6 +750,7 @@ DavicomDevice::_SetupEndpoints() 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; } @@ -589,18 +772,21 @@ DavicomDevice::ReadMACAddress(ether_address_t *address) status_t DavicomDevice::StopDevice() { - /* - status_t result = WriteRXControlRegister(0); + uint8 control = 0; - if(result != B_OK) { - TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result); + // disable RX + status_t result = _ReadRegister(RCR, 1, &control); + if (result != B_OK) { + TRACE_ALWAYS("Error reading RCR: %#010x.\n", result); + return result; } - TRACE_RET(result); + control &= ~RCR_RXEN; + result = _Write1Register(RCR, control); + if (result != B_OK) + TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result); + return result; - */ - TRACE_ALWAYS("Stop device not implemented\n"); - return B_ERROR; } @@ -646,7 +832,7 @@ void DavicomDevice::_ReadCallback(void *cookie, int32 status, void *data, uint32 actualLength) { - TRACE_FLOW("ReadCB: %d bytes; status:%#010x\n", actualLength, status); +// TRACE_RX("ReadCB: %d bytes; status:%#010x\n", actualLength, status); DavicomDevice *device = (DavicomDevice *)cookie; device->fActualLengthRead = actualLength; device->fStatusRead = status; @@ -658,7 +844,7 @@ void DavicomDevice::_WriteCallback(void *cookie, int32 status, void *data, uint32 actualLength) { - TRACE_FLOW("WriteCB: %d bytes; status:%#010x\n", actualLength, status); + TRACE_TX("WriteCB: %d bytes; status:%#010x\n", actualLength, status); DavicomDevice *device = (DavicomDevice *)cookie; device->fActualLengthWrite = actualLength; device->fStatusWrite = status; @@ -700,58 +886,64 @@ DavicomDevice::_NotifyCallback(void *cookie, int32 status, void *data, status_t DavicomDevice::StartDevice() { - uint8 registerValue = 0; + uint8 control = 0; - /* disable loopback */ - status_t result = _ReadRegister(NCR, 1, ®isterValue); + // disable loopback + status_t result = _ReadRegister(NCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading NCR: %#010x.\n", result); return result; } - if (registerValue & NCR_EXT_PHY) + + if (control & NCR_EXT_PHY) TRACE_ALWAYS("Device uses external PHY\n"); - registerValue &= ~NCR_LBK; - result = _Write1Register(NCR, registerValue); + + control &= ~NCR_LBK; + result = _Write1Register(NCR, control); if (result != B_OK) { - TRACE_ALWAYS("Error writing %#02X to NCR: %#010x.\n", registerValue, result); + TRACE_ALWAYS("Error writing %#02X to NCR: %#010x.\n", control, result); return result; } - /* Initialize RX control register */ - result = _ReadRegister(RCR, 1, ®isterValue); + // Initialize RX control register, enable RX and activate multicast + result = _ReadRegister(RCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading RCR: %#010x.\n", result); return result; } - registerValue &= RCR_DIS_LONG & RCR_DIS_CRC & RCR_RXEN; - result = _Write1Register(RCR, registerValue); + + // TODO: do not forget handle promiscous mode correctly in the future!!! + control |= RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN | RCR_ALL; + result = _Write1Register(RCR, control); if (result != B_OK) { - TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", registerValue, result); + TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result); return result; } - /* clear POWER_DOWN state of internal PHY */ - result = _ReadRegister(GPCR, 1, ®isterValue); + // clear POWER_DOWN state of internal PHY + result = _ReadRegister(GPCR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading GPCR: %#010x.\n", result); return result; } - registerValue &= GPCR_GEP_CNTL0; - result = _Write1Register(GPCR, registerValue); + + control |= GPCR_GEP_CNTL0; + result = _Write1Register(GPCR, control); if (result != B_OK) { - TRACE_ALWAYS("Error writing %#02X to GPCR: %#010x.\n", registerValue, result); + TRACE_ALWAYS("Error writing %#02X to GPCR: %#010x.\n", control, result); return result; } - result = _ReadRegister(GPR, 1, ®isterValue); + result = _ReadRegister(GPR, 1, &control); if (result != B_OK) { TRACE_ALWAYS("Error reading GPR: %#010x.\n", result); return result; } - registerValue &= ~GPR_GEP_GEPIO0; - result = _Write1Register(GPR, registerValue); + + control &= ~GPR_GEP_GEPIO0; + result = _Write1Register(GPR, control); if (result != B_OK) { - TRACE_ALWAYS("Error writing %#02X to GPR: %#010x.\n", registerValue, result); + TRACE_ALWAYS("Error writing %#02X to GPR: %#010x.\n", control, result); return result; } @@ -804,6 +996,7 @@ DavicomDevice::OnNotify(uint32 actualLength) return B_OK; } + status_t DavicomDevice::GetLinkState(ether_link_state *linkState) { @@ -838,11 +1031,12 @@ DavicomDevice::GetLinkState(ether_link_state *linkState) if (registerValue & NCR_LBK) linkState->media |= IFM_LOOP; } - - TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n", +/* + TRACE_STATE("Medium state: %s, %lld MBit/s, %s duplex.\n", (linkState->media & IFM_ACTIVE) ? "active" : "inactive", - linkState->speed, + linkState->speed / 1000000, (linkState->media & IFM_FULL_DUPLEX) ? "full" : "half"); +*/ return B_OK; } diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.h b/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.h index f0631925bf..11da5f6566 100644 --- a/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.h +++ b/src/add-ons/kernel/drivers/network/usb_davicom/DavicomDevice.h @@ -1,26 +1,24 @@ /* * Davicom DM9601 USB 1.1 Ethernet Driver. * Copyright (c) 2009 Adrien Destugues + * Copyright (c) 2008, 2011 S.Zharski * Distributed under the terms of the MIT license. * - * 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_DEVICE_H_ #define _USB_Davicom_DEVICE_H_ + #include #include "Driver.h" + class DavicomDevice { public: DavicomDevice(usb_device device, const char *description); @@ -57,10 +55,14 @@ static void _NotifyCallback(void *cookie, int32 status, status_t _ReadRegister(uint8 reg, size_t size, uint8* buffer); status_t _WriteRegister(uint8 reg, size_t size, uint8* buffer); status_t _Write1Register(uint8 reg, uint8 buffer); + status_t _ReadMII(uint8 reg, uint16* data); + status_t _WriteMII(uint8 reg, uint16 data); + status_t _InitMII(); + status_t _EnableInterrupts(bool enable); static const int kFrameSize = 1518; -static const int kRXHeaderSize = 3; -static const int kTXHeaderSize = 2; +static const size_t kRXHeaderSize = 3; +static const size_t kTXHeaderSize = 2; protected: /* overrides */ @@ -87,6 +89,7 @@ const char * fDescription; usb_pipe fNotifyEndpoint; usb_pipe fReadEndpoint; usb_pipe fWriteEndpoint; + uint16 fMaxTXPacketSize; // data stores for async usb transfers uint32 fActualLengthRead; @@ -97,7 +100,7 @@ const char * fDescription; sem_id fNotifyWriteSem; uint8 * fNotifyBuffer; -static const int kNotifyBufferSize = 8; +static const size_t kNotifyBufferSize = 8; // connection data sem_id fLinkStateChangeSem; diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/Settings.cpp b/src/add-ons/kernel/drivers/network/usb_davicom/Settings.cpp index e32d7d1e4a..311653d86a 100644 --- a/src/add-ons/kernel/drivers/network/usb_davicom/Settings.cpp +++ b/src/add-ons/kernel/drivers/network/usb_davicom/Settings.cpp @@ -17,7 +17,9 @@ bool gTraceOn = false; bool gTruncateLogFile = false; bool gAddTimeStamp = true; -bool gTraceFlow = false; +bool gTraceState = false; +bool gTraceRX = false; +bool gTraceTX = false; static char *gLogFilePath = NULL; mutex gLogLock; @@ -40,7 +42,9 @@ void load_settings() return; gTraceOn = get_driver_boolean_parameter(handle, "trace", gTraceOn, true); - gTraceFlow = get_driver_boolean_parameter(handle, "trace_flow", gTraceFlow, true); + gTraceState = get_driver_boolean_parameter(handle, "trace_state", gTraceState, true); + gTraceRX = get_driver_boolean_parameter(handle, "trace_rx", gTraceRX, true); + gTraceTX = get_driver_boolean_parameter(handle, "trace_tx", gTraceTX, true); gTruncateLogFile = get_driver_boolean_parameter(handle, "truncate_logfile", gTruncateLogFile, true); gAddTimeStamp = get_driver_boolean_parameter(handle, "add_timestamp", diff --git a/src/add-ons/kernel/drivers/network/usb_davicom/Settings.h b/src/add-ons/kernel/drivers/network/usb_davicom/Settings.h index 985057d011..a4a456d258 100644 --- a/src/add-ons/kernel/drivers/network/usb_davicom/Settings.h +++ b/src/add-ons/kernel/drivers/network/usb_davicom/Settings.h @@ -25,8 +25,14 @@ 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) +extern bool gTraceState; +#define TRACE_STATE(x...) usb_davicom_trace(gTraceState, NULL, x) + +extern bool gTraceRX; +#define TRACE_RX(x...) usb_davicom_trace(gTraceRX, NULL, x) + +extern bool gTraceTX; +#define TRACE_TX(x...) usb_davicom_trace(gTraceTX, NULL, x) #define TRACE_RET(result) usb_davicom_trace(false, __func__, \ "Returns:%#010x\n", result); 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 index 4dc7c7f37e..9399a7af5d 100644 --- a/src/add-ons/kernel/drivers/network/usb_davicom/usb_davicom.settings +++ b/src/add-ons/kernel/drivers/network/usb_davicom/usb_davicom.settings @@ -28,8 +28,20 @@ reset_logfile on # add_timestamp off -## trace_flow [on|off] - activate data flow tracing. Statistic about of -## transferred data amount and media state. +## trace_state [on|off] - activate state tracing. Statistic about of +## media state. ## default value: off -# trace_flow on +# trace_state on + +## trace_rx [on|off] - activate data receivening tracing. Statistic about of +## transferred data amount. +## default value: off + +# trace_rx on + +## trace_tx [on|off] - activate data transmitting tracing. Statistic about of +## transferred data amount. +## default value: off + +# trace_tx on