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
AX88172Device(usb_device device,DeviceInfo & deviceInfo)126 AX88172Device::AX88172Device(usb_device device, DeviceInfo& deviceInfo)
127 :
128 ASIXDevice(device, deviceInfo)
129 {
130 fStatus = InitDevice();
131 }
132
133
134 status_t
InitDevice()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
SetupDevice(bool deviceReplugged)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
StartDevice()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
OnNotify(uint32 actualLength)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
GetLinkState(ether_link_state * linkState)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