1 /* 2 * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver. 3 * Copyright (c) 2008, 2011 S.Zharski <imker@gmx.li> 4 * Distributed under the terms of the MIT license. 5 * 6 * Heavily based on code of the 7 * Driver for USB Ethernet Control Model devices 8 * Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch> 9 * Distributed under the terms of the MIT license. 10 * 11 */ 12 13 14 #include "AX88172Device.h" 15 16 #include <net/if_media.h> 17 18 #include "ASIXVendorRequests.h" 19 #include "Settings.h" 20 21 22 // Most of vendor requests for all supported chip types use the same 23 // constants (see ASIXVendorRequests.h) but the layout of request data 24 // may be slightly diferrent for specific chip type. Below is a quick 25 // reference for AX88172 vendor requests data layout. 26 // 27 // READ_RXTX_SRAM, // C0 02 XX YY 0M 00 0200 Read Rx/Tx SRAM 28 // M = 0 : Rx, M=1 : Tx 29 // WRITE_RX_SRAM, // 40 03 XX YY PP QQ 0000 Write Rx SRAM 30 // WRITE_TX_SRAM, // 40 04 XX YY PP QQ 0000 Write Tx SRAM 31 // SW_MII_OP, // 40 06 00 00 00 00 0000 Software MII Operation 32 // READ_MII, // C0 07 PI 00 RG 00 0200 Read MII Register 33 // WRITE_MII, // 40 08 PI 00 RG 00 0200 Write MII Register 34 // READ_MII_OP_MODE, // C0 09 00 00 00 00 0100 Read MII Operation Mode 35 // HW_MII_OP, // 40 0A 00 00 00 00 0000 Hardware MII Operation 36 // READ_SROM, // C0 0B DR 00 00 00 0200 Read SROM 37 // WRITE_SROM, // 40 0C DR 00 MM SS 0000 Write SROM 38 // WRITE_SROM_ENABLE, // 40 0D 00 00 00 00 0000 Write SROM Enable 39 // WRITE_SROM_DISABLE, // 40 0E 00 00 00 00 0000 Write SROM Disable 40 // READ_RX_CONTROL, // C0 0F 00 00 00 00 0200 Read Rx Control Register 41 // WRITE_RX_CONTROL, // 40 10 RR 00 00 00 0000 Write Rx Control Register 42 // READ_IPGS, // C0 11 00 00 00 00 0300 Read IPG/IPG1/IPG2 Register 43 // WRITE_IPG0, // 40 12 II 00 00 00 0000 Write IPG Register 44 // WRITE_IPG1, // 40 13 II 00 00 00 0000 Write IPG1 Register 45 // WRITE_IPG2, // 40 14 II 00 00 00 0000 Write IPG2 Register 46 // READ_MF_ARRAY, // C0 15 00 00 00 00 0800 Read Multi-Filter Array 47 // WRITE_MF_ARRAY, // 40 16 00 00 00 00 0800 Write Multi-Filter Array 48 // READ_NODEID, // C0 17 00 00 00 00 0600 Read Node ID 49 // WRITE_NODEID, // 50 // READ_PHYID, // C0 19 00 00 00 00 0200 Read Ethernet/HomePNA PhyID 51 // READ_MEDIUM_STATUS, // C0 1A 00 00 00 00 0100 Read Medium Status 52 // WRITE_MEDIUM_MODE, // 40 1B MM 00 00 00 0000 Write Medium Mode 53 // GET_MONITOR_MODE, // C0 1C 00 00 00 00 0100 Get Monitor Mode Status 54 // SET_MONITOR_MODE, // 40 1D MM 00 00 00 0000 Set Monitor Mode On/Off 55 // READ_GPIOS, // C0 1E 00 00 00 00 0100 Read GPIOs 56 // WRITE_GPIOS, // 40 1F MM 00 00 00 0000 Write GPIOs 57 58 // RX Control Register bits 59 // RXCTL_PROMISCUOUS, // forward all frames up to the host 60 // RXCTL_ALL_MULTICAT, // forward all multicast frames up to the host 61 // RXCTL_UNICAST, // ??? 62 // RXCTL_BROADCAST, // forward broadcast frames up to the host 63 // RXCTL_MULTICAST, // forward all multicast frames that are 64 // matching to multicast filter up to the host 65 // RXCTL_START, // ethernet MAC start operating 66 67 68 // PHY IDs request answer data layout 69 struct PhyIDs { 70 uint8 PhyID1; 71 uint8 PhyID2; 72 } _PACKED; 73 74 75 // Medium state bits 76 enum AX88172_MediumState { 77 MEDIUM_STATE_FULL_DUPLEX = 0x02, 78 MEDIUM_STATE_TX_ABORT_ALLOW = 0x04, 79 MEDIUM_STATE_FLOW_CONTOL_EN = 0x10 80 }; 81 82 83 // Monitor Mode bits 84 enum AX88172_MonitorMode { 85 MONITOR_MODE = 0x01, 86 MONITOR_MODE_LINK_UP_WAKE = 0x02, 87 MONITOR_MODE_MAGIC_PACKET_EN = 0x04, 88 MONITOR_MODE_HS_FS = 0x10 89 }; 90 91 92 // General Purpose I/O Register 93 enum AX88172_GPIO { 94 GPIO_OO_0EN = 0x01, 95 GPIO_IO_0 = 0x02, 96 GPIO_OO_1EN = 0x04, 97 GPIO_IO_1 = 0x08, 98 GPIO_OO_2EN = 0x10, 99 GPIO_IO_2 = 0x20 100 }; 101 102 103 // Notification data layout 104 struct AX88172Notify { 105 uint8 btA1; 106 uint8 bt01; 107 uint8 btNN; // AX88172_LinkState below 108 uint8 bt03; 109 uint8 bt04; 110 uint8 bt80; // 90h 111 uint8 bt06; 112 uint8 bt07; 113 } _PACKED; 114 115 116 // Link-State bits 117 enum AX88172_LinkState { 118 LINK_STATE_PHY1 = 0x01, 119 LINK_STATE_PHY2 = 0x02 120 }; 121 122 123 const uint16 maxFrameSize = 1518; 124 125 126 AX88172Device::AX88172Device(usb_device device, DeviceInfo& deviceInfo) 127 : 128 ASIXDevice(device, deviceInfo) 129 { 130 fStatus = InitDevice(); 131 } 132 133 134 status_t 135 AX88172Device::InitDevice() 136 { 137 fFrameSize = maxFrameSize; 138 139 fReadNodeIDRequest = READ_NODEID_AX88172; 140 141 fNotifyBufferLength = sizeof(AX88172Notify); 142 fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength); 143 if (fNotifyBuffer == NULL) { 144 TRACE_ALWAYS("Error of allocating memory for notify buffer.\n"); 145 return B_NO_MEMORY; 146 } 147 148 TRACE_RET(B_OK); 149 return B_OK; 150 } 151 152 153 status_t 154 AX88172Device::SetupDevice(bool deviceReplugged) 155 { 156 status_t result = ASIXDevice::SetupDevice(deviceReplugged); 157 if (result != B_OK) { 158 return result; 159 } 160 161 result = fMII.Init(fDevice); 162 163 if (result == B_OK) 164 return fMII.SetupPHY(); 165 166 TRACE_RET(result); 167 return result; 168 } 169 170 171 status_t 172 AX88172Device::StartDevice() 173 { 174 size_t actualLength = 0; 175 176 for (size_t i = 0; i < sizeof(fIPG) / sizeof(fIPG[0]); i++) { 177 status_t result = gUSBModule->send_request(fDevice, 178 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_IPG0, 179 0, 0, sizeof(fIPG[i]), &fIPG[i], &actualLength); 180 181 if (result != B_OK) { 182 TRACE_ALWAYS("Error writing IPG%d: %#010x\n", i, result); 183 return result; 184 } 185 186 if (actualLength != sizeof(fIPG[i])) { 187 TRACE_ALWAYS("Mismatch of written IPG%d data. " 188 "%d bytes of %d written.\n", i, actualLength, sizeof(fIPG[i])); 189 } 190 } 191 192 uint16 rxcontrol = RXCTL_START | RXCTL_UNICAST | RXCTL_BROADCAST; 193 status_t result = WriteRXControlRegister(rxcontrol); 194 if (result != B_OK) { 195 TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 196 rxcontrol, result); 197 } 198 199 TRACE_RET(result); 200 return result; 201 } 202 203 204 status_t 205 AX88172Device::OnNotify(uint32 actualLength) 206 { 207 if (actualLength < sizeof(AX88172Notify)) { 208 TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n", 209 actualLength, sizeof(AX88172Notify)); 210 return B_BAD_DATA; 211 } 212 213 AX88172Notify *notification = (AX88172Notify *)fNotifyBuffer; 214 215 if (notification->btA1 != 0xa1) { 216 TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n", 217 notification->btA1); 218 } 219 220 uint phyIndex = 0; 221 bool linkIsUp = fHasConnection; 222 switch(fMII.ActivePHY()) { 223 case PrimaryPHY: 224 phyIndex = 1; 225 linkIsUp = (notification->btNN & LINK_STATE_PHY1) 226 == LINK_STATE_PHY1; 227 break; 228 case SecondaryPHY: 229 phyIndex = 2; 230 linkIsUp = (notification->btNN & LINK_STATE_PHY2) 231 == LINK_STATE_PHY2; 232 break; 233 default: 234 case CurrentPHY: 235 TRACE_ALWAYS("Error: PHY is not initialized.\n"); 236 return B_NO_INIT; 237 } 238 239 bool linkStateChange = linkIsUp != fHasConnection; 240 fHasConnection = linkIsUp; 241 242 if (linkStateChange) { 243 TRACE("Link state of PHY%d has been changed to '%s'\n", 244 phyIndex, fHasConnection ? "up" : "down"); 245 } 246 247 if (linkStateChange && fLinkStateChangeSem >= B_OK) 248 release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); 249 250 return B_OK; 251 } 252 253 254 status_t 255 AX88172Device::GetLinkState(ether_link_state *linkState) 256 { 257 uint16 miiANAR = 0; 258 uint16 miiANLPAR = 0; 259 260 status_t result = fMII.Read(MII_ANAR, &miiANAR); 261 if (result != B_OK) { 262 TRACE_ALWAYS("Error reading MII ANAR register:%#010x\n", result); 263 return result; 264 } 265 266 result = fMII.Read(MII_ANLPAR, &miiANLPAR); 267 if (result != B_OK) { 268 TRACE_ALWAYS("Error reading MII ANLPAR register:%#010x\n", result); 269 return result; 270 } 271 272 TRACE_FLOW("ANAR:%04x ANLPAR:%04x\n", miiANAR, miiANLPAR); 273 274 uint16 mediumStatus = miiANAR & miiANLPAR; 275 276 linkState->quality = 1000; 277 278 linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0); 279 linkState->media |= mediumStatus & (ANLPAR_TX_FD | ANLPAR_10_FD) 280 ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX; 281 282 linkState->speed = mediumStatus & (ANLPAR_TX_FD | ANLPAR_TX_HD) 283 ? 100000000 : 10000000; 284 285 TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n", 286 (linkState->media & IFM_ACTIVE) ? "active" : "inactive", 287 linkState->speed / 1000000, 288 (linkState->media & IFM_FULL_DUPLEX) ? "full" : "half"); 289 return B_OK; 290 } 291