/* * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver. * Copyright (c) 2008, 2011 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 "AX88172Device.h" #include #include "ASIXVendorRequests.h" #include "Settings.h" // Most of vendor requests for all supported chip types use the same // constants (see ASIXVendorRequests.h) but the layout of request data // may be slightly diferrent for specific chip type. Below is a quick // reference for AX88172 vendor requests data layout. // // READ_RXTX_SRAM, // C0 02 XX YY 0M 00 0200 Read Rx/Tx SRAM // M = 0 : Rx, M=1 : Tx // WRITE_RX_SRAM, // 40 03 XX YY PP QQ 0000 Write Rx SRAM // WRITE_TX_SRAM, // 40 04 XX YY PP QQ 0000 Write Tx SRAM // SW_MII_OP, // 40 06 00 00 00 00 0000 Software MII Operation // READ_MII, // C0 07 PI 00 RG 00 0200 Read MII Register // WRITE_MII, // 40 08 PI 00 RG 00 0200 Write MII Register // READ_MII_OP_MODE, // C0 09 00 00 00 00 0100 Read MII Operation Mode // HW_MII_OP, // 40 0A 00 00 00 00 0000 Hardware MII Operation // READ_SROM, // C0 0B DR 00 00 00 0200 Read SROM // WRITE_SROM, // 40 0C DR 00 MM SS 0000 Write SROM // WRITE_SROM_ENABLE, // 40 0D 00 00 00 00 0000 Write SROM Enable // WRITE_SROM_DISABLE, // 40 0E 00 00 00 00 0000 Write SROM Disable // READ_RX_CONTROL, // C0 0F 00 00 00 00 0200 Read Rx Control Register // WRITE_RX_CONTROL, // 40 10 RR 00 00 00 0000 Write Rx Control Register // READ_IPGS, // C0 11 00 00 00 00 0300 Read IPG/IPG1/IPG2 Register // WRITE_IPG0, // 40 12 II 00 00 00 0000 Write IPG Register // WRITE_IPG1, // 40 13 II 00 00 00 0000 Write IPG1 Register // WRITE_IPG2, // 40 14 II 00 00 00 0000 Write IPG2 Register // READ_MF_ARRAY, // C0 15 00 00 00 00 0800 Read Multi-Filter Array // WRITE_MF_ARRAY, // 40 16 00 00 00 00 0800 Write Multi-Filter Array // READ_NODEID, // C0 17 00 00 00 00 0600 Read Node ID // WRITE_NODEID, // // READ_PHYID, // C0 19 00 00 00 00 0200 Read Ethernet/HomePNA PhyID // READ_MEDIUM_STATUS, // C0 1A 00 00 00 00 0100 Read Medium Status // WRITE_MEDIUM_MODE, // 40 1B MM 00 00 00 0000 Write Medium Mode // GET_MONITOR_MODE, // C0 1C 00 00 00 00 0100 Get Monitor Mode Status // SET_MONITOR_MODE, // 40 1D MM 00 00 00 0000 Set Monitor Mode On/Off // READ_GPIOS, // C0 1E 00 00 00 00 0100 Read GPIOs // WRITE_GPIOS, // 40 1F MM 00 00 00 0000 Write GPIOs // RX Control Register bits // RXCTL_PROMISCUOUS, // forward all frames up to the host // RXCTL_ALL_MULTICAT, // forward all multicast frames up to the host // RXCTL_UNICAST, // ??? // RXCTL_BROADCAST, // forward broadcast frames up to the host // RXCTL_MULTICAST, // forward all multicast frames that are // matching to multicast filter up to the host // RXCTL_START, // ethernet MAC start operating // PHY IDs request answer data layout struct PhyIDs { uint8 PhyID1; uint8 PhyID2; } _PACKED; // Medium state bits enum AX88172_MediumState { MEDIUM_STATE_FULL_DUPLEX = 0x02, MEDIUM_STATE_TX_ABORT_ALLOW = 0x04, MEDIUM_STATE_FLOW_CONTOL_EN = 0x10 }; // Monitor Mode bits enum AX88172_MonitorMode { MONITOR_MODE = 0x01, MONITOR_MODE_LINK_UP_WAKE = 0x02, MONITOR_MODE_MAGIC_PACKET_EN = 0x04, MONITOR_MODE_HS_FS = 0x10 }; // General Purpose I/O Register enum AX88172_GPIO { GPIO_OO_0EN = 0x01, GPIO_IO_0 = 0x02, GPIO_OO_1EN = 0x04, GPIO_IO_1 = 0x08, GPIO_OO_2EN = 0x10, GPIO_IO_2 = 0x20 }; // Notification data layout struct AX88172Notify { uint8 btA1; uint8 bt01; uint8 btNN; // AX88172_LinkState below uint8 bt03; uint8 bt04; uint8 bt80; // 90h uint8 bt06; uint8 bt07; } _PACKED; // Link-State bits enum AX88172_LinkState { LINK_STATE_PHY1 = 0x01, LINK_STATE_PHY2 = 0x02 }; const uint16 maxFrameSize = 1518; AX88172Device::AX88172Device(usb_device device, DeviceInfo& deviceInfo) : ASIXDevice(device, deviceInfo) { fStatus = InitDevice(); } status_t AX88172Device::InitDevice() { fFrameSize = maxFrameSize; fReadNodeIDRequest = READ_NODEID_AX88172; fNotifyBufferLength = sizeof(AX88172Notify); fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength); if (fNotifyBuffer == NULL) { TRACE_ALWAYS("Error of allocating memory for notify buffer.\n"); return B_NO_MEMORY; } TRACE_RET(B_OK); return B_OK; } status_t AX88172Device::SetupDevice(bool deviceReplugged) { status_t result = ASIXDevice::SetupDevice(deviceReplugged); if (result != B_OK) { return result; } result = fMII.Init(fDevice); if (result == B_OK) return fMII.SetupPHY(); TRACE_RET(result); return result; } status_t AX88172Device::StartDevice() { size_t actualLength = 0; for (size_t i = 0; i < sizeof(fIPG) / sizeof(fIPG[0]); i++) { status_t result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_IPG0, 0, 0, sizeof(fIPG[i]), &fIPG[i], &actualLength); if (result != B_OK) { TRACE_ALWAYS("Error writing IPG%d: %#010x\n", i, result); return result; } if (actualLength != sizeof(fIPG[i])) { TRACE_ALWAYS("Mismatch of written IPG%d data. " "%d bytes of %d written.\n", i, actualLength, sizeof(fIPG[i])); } } uint16 rxcontrol = RXCTL_START | RXCTL_UNICAST | RXCTL_BROADCAST; status_t 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 AX88172Device::OnNotify(uint32 actualLength) { if (actualLength < sizeof(AX88172Notify)) { TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n", actualLength, sizeof(AX88172Notify)); return B_BAD_DATA; } AX88172Notify *notification = (AX88172Notify *)fNotifyBuffer; if (notification->btA1 != 0xa1) { TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n", notification->btA1); } uint phyIndex = 0; bool linkIsUp = fHasConnection; switch(fMII.ActivePHY()) { case PrimaryPHY: phyIndex = 1; linkIsUp = (notification->btNN & LINK_STATE_PHY1) == LINK_STATE_PHY1; break; case SecondaryPHY: phyIndex = 2; linkIsUp = (notification->btNN & LINK_STATE_PHY2) == LINK_STATE_PHY2; break; default: case CurrentPHY: TRACE_ALWAYS("Error: PHY is not initialized.\n"); return B_NO_INIT; } bool linkStateChange = linkIsUp != fHasConnection; fHasConnection = linkIsUp; if (linkStateChange) { TRACE("Link state of PHY%d has been changed to '%s'\n", phyIndex, fHasConnection ? "up" : "down"); } if (linkStateChange && fLinkStateChangeSem >= B_OK) release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); return B_OK; } status_t AX88172Device::GetLinkState(ether_link_state *linkState) { uint16 miiANAR = 0; uint16 miiANLPAR = 0; status_t result = fMII.Read(MII_ANAR, &miiANAR); if (result != B_OK) { TRACE_ALWAYS("Error reading MII ANAR register:%#010x\n", result); return result; } result = fMII.Read(MII_ANLPAR, &miiANLPAR); if (result != B_OK) { TRACE_ALWAYS("Error reading MII ANLPAR register:%#010x\n", result); return result; } TRACE_FLOW("ANAR:%04x ANLPAR:%04x\n", miiANAR, miiANLPAR); uint16 mediumStatus = miiANAR & miiANLPAR; linkState->quality = 1000; linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0); linkState->media |= mediumStatus & (ANLPAR_TX_FD | ANLPAR_10_FD) ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX; linkState->speed = mediumStatus & (ANLPAR_TX_FD | ANLPAR_TX_HD) ? 100000000 : 10000000; TRACE_FLOW("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"); return B_OK; }