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
This commit is contained in:
Siarzhuk Zharski 2011-09-12 19:35:28 +00:00
parent ed2e27f066
commit b337f35c45
5 changed files with 288 additions and 69 deletions

View File

@ -1,22 +1,21 @@
/*
* Davicom DM9601 USB 1.1 Ethernet Driver.
* Copyright (c) 2009 Adrien Destugues <pulkomandy@gmail.com>
* Copyright (c) 2008, 2011 Siarzhuk Zharski <imker@gmx.li>
* 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 <imker@gmx.li>
* 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 <mmlr@mlotz.ch>
* 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, &registerValue);
// 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, &registerValue);
// 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, &registerValue);
// 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, &registerValue);
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;
}

View File

@ -1,26 +1,24 @@
/*
* Davicom DM9601 USB 1.1 Ethernet Driver.
* Copyright (c) 2009 Adrien Destugues <pulkomandy@gmail.com>
* Copyright (c) 2008, 2011 S.Zharski <imker@gmx.li>
* Distributed under the terms of the MIT license.
*
* ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
* Copyright (c) 2008 S.Zharski <imker@gmx.li>
* 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 <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _USB_Davicom_DEVICE_H_
#define _USB_Davicom_DEVICE_H_
#include <net/if_media.h>
#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;

View File

@ -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",

View File

@ -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);

View File

@ -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