xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_asix/AX88178Device.cpp (revision 899e0ef82b5624ace2ccfa5f5a58c8ebee54aaef)
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 "AX88178Device.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 AX88178 vendor requests data layout.
26 
27 // READ_RXTX_SRAM,		//C002_AA0B_0C00_0800 Rx/Tx SRAM Read
28 // WRITE_RXTX_SRAM,		//4003_AA0B_0C00_0800 Rx/Tx SRAM Write
29 // SW_MII_OP,			//4006_0000_0000_0000 SW Serial Management Control
30 // READ_MII,			//c007_aa00_cc00_0200 PHY Read
31 // WRITE_MII,			//4008_aa00_cc00_0200 PHY Write
32 // READ_MII_STATUS,		//c009_0000_0000_0100 Serial Management Status
33 // HW_MII_OP,			//400a_0000_0000_0000 HW Serial Management Control
34 // READ_SROM,			//C00B_AA00_0000_0200 SROM Read
35 // WRITE_SROM,			//400C_AA00_CCDD_0000 SROM Write
36 // WRITE_SROM_ENABLE,	//400D_0000_0000_0000 SROM Write Enable
37 // WRITE_SROM_DISABLE,	//400E_0000_0000_0000 SROM Write Disable
38 // READ_RX_CONTROL,		//C00F_0000_0000_0200 Read Rx Control
39 // WRITE_RX_CONTROL,	//4010_AABB_0000_0000 Write Rx Control
40 // READ_IPGS,			//C011_0000_0000_0300 Read IPG/IPG1/IPG2 Register
41 // WRITE_IPGS,			//4012_AABB_CC00_0000 Write IPG/IPG1/IPG2 Register
42 // READ_NODEID,			//C013_0000_0000_0600 Read Node ID
43 // WRITE_NODEID,		//4014_0000_0000_0600 Write Node ID
44 // READ_MF_ARRAY,		//C015_0000_0000_0800 Read Multicast Filter Array
45 // WRITE_MF_ARRAY,		//4016_0000_0000_0800 Write Multicast Filter Array
46 // READ_TEST,			//4017_AA00_0000_0000 Write Test Register
47 // READ_PHYID,			//C019_0000_0000_0200 Read Ethernet/HomePNA PHY Address
48 // READ_MEDIUM_STATUS,	//C01A_0000_0000_0200 Read Medium Status
49 // WRITE_MEDIUM_MODE,	//401B_AABB_0000_0000 Write Medium Mode Register
50 // GET_MONITOR_MODE,	//C01C_0000_0000_0100 Read Monitor Mode Status
51 // SET_MONITOR_MODE,	//401D_AA00_0000_0000 Write Monitor Mode Register
52 // READ_GPIOS,			//C01E_0000_0000_0100 Read GPIOs Status
53 // WRITE_GPIOS,			//401F_AA00_0000_0000 Write GPIOs
54 // WRITE_SOFT_RESET,	//4020_AA00_0000_0000 Write Software Reset
55 // READ_MIIS_IF_STATE,	//C021_AA00_0000_0100 Read MII/GMII/RGMII Iface Status
56 // WRITE_MIIS_IF_STATE,	//4022_AA00_0000_0000 Write MII/GMII/RGMII Iface Control
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_SEP,			// forward frames with CRC error up to the host
62 // RXCTL_BROADCAST,		// forward broadcast frames up to the host
63 // RXCTL_MULTICAST,		// forward multicast frames that are
64 //							matching to multicast filter up to the host
65 // RXCTL_AP,			// forward unicast frames that are matching
66 //							to multicast filter up to the host
67 // RXCTL_START,			// ethernet MAC start operating
68 // RXCTL_USB_MFB,		// Max Frame Burst TX on USB
69 
70 
71 // PHY IDs request answer data layout
72 struct AX88178_PhyIDs {
73 	uint8 SecPhyID;
74 	uint8 PriPhyID2;
75 } _PACKED;
76 
77 
78 // Medium state bits
79 enum AX88178_MediumState {
80 	MEDIUM_STATE_GM		= 0x0001,
81 	MEDIUM_STATE_FD		= 0x0002,
82 	MEDIUM_STATE_AC		= 0x0004, // must be always set
83 	MEDIUM_STATE_ENCK	= 0x0008,
84 	MEDIUM_STATE_RFC	= 0x0010,
85 	MEDIUM_STATE_TFC	= 0x0020,
86 	MEDIUM_STATE_JFE	= 0x0040,
87 	MEDIUM_STATE_PF_ON	= 0x0080,
88 	MEDIUM_STATE_PF_OFF	= 0x0000,
89 	MEDIUM_STATE_RE	   	= 0x0100,
90 	MEDIUM_STATE_PS_100	= 0x0200,
91 	MEDIUM_STATE_PS_10 	= 0x0000,
92 	MEDIUM_STATE_SBP1  	= 0x0800,
93 	MEDIUM_STATE_SBP0  	= 0x0000,
94 	MEDIUM_STATE_SM_ON 	= 0x1000
95 };
96 
97 
98 // Monitor Mode bits
99 enum AX88178_MonitorMode {
100 	MONITOR_MODE_MOM	= 0x01,
101 	MONITOR_MODE_RWLU	= 0x02,
102 	MONITOR_MODE_RWMP	= 0x04,
103 	MONITOR_MODE_US 	= 0x10
104 };
105 
106 
107 // General Purpose I/O Register
108 enum AX88178_GPIO {
109 	GPIO_OO_0EN	= 0x01,
110 	GPIO_IO_0	= 0x02,
111 	GPIO_OO_1EN	= 0x04,
112 	GPIO_IO_1	= 0x08,
113 	GPIO_OO_2EN	= 0x10,
114 	GPIO_IO_2	= 0x20,
115 	GPIO_RSE	= 0x80
116 };
117 
118 
119 // Software Reset Register bits
120 enum AX88178_SoftwareReset {
121 	SW_RESET_RR		= 0x01,
122 	SW_RESET_RT		= 0x02,
123 	SW_RESET_PRTE	= 0x04,
124 	SW_RESET_PRL	= 0x08,
125 	SW_RESET_BZ		= 0x10,
126 	SW_RESET_BIT6	= 0x40 // always set to 1
127 };
128 
129 
130 // MII/GMII/RGMII Interface Conttrol
131 enum AX88178_MIISInterfaceStatus {
132 	MIIS_IF_STATE_DM	= 0x01,
133 	MIIS_IF_STATE_RB	= 0x02
134 };
135 
136 
137 // Notification data layout
138 struct AX88178_Notify {
139 	uint8  btA1;
140 	uint8  bt01;
141 	uint8  btBB; // AX88178_BBState below
142 	uint8  bt03;
143 	uint16 regCCDD;
144 	uint16 regEEFF;
145 } _PACKED;
146 
147 
148 // Link-State bits
149 enum AX88178_BBState {
150 	LINK_STATE_PPLS		= 0x01,
151 	LINK_STATE_SPLS		= 0x02,
152 	LINK_STATE_FLE		= 0x04,
153 	LINK_STATE_MDINT	= 0x08
154 };
155 
156 
157 const uint16 maxFrameSize = 1536;
158 
159 
160 AX88178Device::AX88178Device(usb_device device, DeviceInfo& deviceInfo)
161 	:
162 	ASIXDevice(device, deviceInfo)
163 {
164 	fStatus = InitDevice();
165 }
166 
167 
168 status_t
169 AX88178Device::InitDevice()
170 {
171 	fFrameSize = maxFrameSize;
172 	fUseTRXHeader = true;
173 
174 	fReadNodeIDRequest = READ_NODEID;
175 
176 	fNotifyBufferLength = sizeof(AX88178_Notify);
177 	fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
178 	if (fNotifyBuffer == NULL) {
179 		TRACE_ALWAYS("Error of allocating memory for notify buffer.\n");
180 		return B_NO_MEMORY;
181 	}
182 
183 	TRACE_RET(B_OK);
184 	return B_OK;
185 }
186 
187 
188 status_t
189 AX88178Device::SetupDevice(bool deviceReplugged)
190 {
191 	status_t result = ASIXDevice::SetupDevice(deviceReplugged);
192 	if (result != B_OK) {
193 		return result;
194 	}
195 
196 	result = fMII.Init(fDevice);
197 
198 	if (result != B_OK) {
199 		return result;
200 	}
201 
202 	size_t actualLength = 0;
203 	// get the "magic" word from EEPROM
204 	result = gUSBModule->send_request(fDevice,
205 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SROM_ENABLE,
206 		0, 0, 0, 0, &actualLength);
207 
208 	if (result != B_OK) {
209 		TRACE_ALWAYS("Error of enabling SROM access:%#010x\n", result);
210 		return result;
211 	}
212 
213 	uint16 eepromData = 0;
214 	status_t op_result = gUSBModule->send_request(fDevice,
215 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_SROM,
216 		0x17, 0, sizeof(eepromData), &eepromData, &actualLength);
217 
218 	if (op_result != B_OK) {
219 		TRACE_ALWAYS("Error of reading SROM data:%#010x\n", result);
220 	}
221 
222 	if (actualLength != sizeof(eepromData)) {
223 		TRACE_ALWAYS("Mismatch of reading SROM data."
224 			"Read %d bytes instead of %d\n", actualLength, sizeof(eepromData));
225 	}
226 
227 	result = gUSBModule->send_request(fDevice,
228 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SROM_DISABLE,
229 		0, 0, 0, 0, &actualLength);
230 
231 	if (result != B_OK) {
232 		TRACE_ALWAYS("Error of disabling SROM access: %#010x\n", result);
233 		return result;
234 	}
235 
236 	if (op_result != B_OK) {
237 		return op_result;
238 	}
239 
240 	// some shaman's dances with GPIO
241 	struct GPIOData {
242 		bigtime_t	delay;
243 		uint16 		value;
244 	} GPIOCommands[] = {
245 		// eeprom bit 8 is off
246 		{ 40000  , GPIO_OO_1EN | GPIO_IO_1 | GPIO_RSE                },
247 		{ 30000  , GPIO_OO_2EN | GPIO_IO_2 | GPIO_OO_1EN | GPIO_IO_1 },
248 		{ 300000 , GPIO_OO_2EN |             GPIO_OO_1EN | GPIO_IO_1 },
249 		{ 30000  , GPIO_OO_2EN | GPIO_IO_2 | GPIO_OO_1EN | GPIO_IO_1 },
250 		// eeprom bit 8 is on
251 		{ 40000  , GPIO_OO_1EN | GPIO_IO_1 | GPIO_RSE },
252 		{ 30000  , GPIO_OO_1EN                        },
253 		{ 30000  , GPIO_OO_1EN | GPIO_IO_1            },
254 	};
255 
256 	bool bCase8 = (eepromData >> 8) != 1;
257 	size_t from = bCase8 ? 0 : 4;
258 	size_t to   = bCase8 ? 3 : 6;
259 
260 	for (size_t i = from; i <= to; i++) {
261 		result = gUSBModule->send_request(fDevice,
262 			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_GPIOS,
263 			GPIOCommands[i].value, 0, 0, 0, &actualLength);
264 
265 		snooze(GPIOCommands[i].delay);
266 
267 		if (result != B_OK) {
268 			TRACE_ALWAYS("Error of GPIO setup command %d:[%#04x]: %#010x\n",
269 				i, GPIOCommands[i].value, result);
270 			return result;
271 		}
272 	}
273 
274 	uint8 uSWReset = 0;
275 	// finally a bit of exercises for SW reset register...
276 	result = gUSBModule->send_request(fDevice,
277 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SOFT_RESET,
278 		uSWReset, 0, 0, 0, &actualLength);
279 
280 	if (result != B_OK) {
281 		TRACE_ALWAYS("Error of SW reset to %#02x: %#010x\n", uSWReset, result);
282 		return result;
283 	}
284 
285 	snooze(150000);
286 
287 	uSWReset = SW_RESET_PRL | SW_RESET_BIT6;
288 	result = gUSBModule->send_request(fDevice,
289 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SOFT_RESET,
290 		uSWReset, 0, 0, 0, &actualLength);
291 
292 	if (result != B_OK) {
293 		TRACE_ALWAYS("Error of SW reset to %#02x: %#010x\n", uSWReset, result);
294 		return result;
295 	}
296 
297 	snooze(150000);
298 
299 	result = WriteRXControlRegister(0);
300 	if (result != B_OK) {
301 		TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result);
302 		return result;
303 	}
304 
305 	result = fMII.SetupPHY();
306 
307 	TRACE_RET(result);
308 	return result;
309 }
310 
311 
312 status_t
313 AX88178Device::StartDevice()
314 {
315 	size_t actualLength = 0;
316 	status_t result = gUSBModule->send_request(fDevice,
317 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_IPGS,
318 		0, 0, sizeof(fIPG), fIPG, &actualLength);
319 
320 	if (result != B_OK) {
321 		TRACE_ALWAYS("Error of writing IPGs:%#010x\n", result);
322 		return result;
323 	}
324 
325 	if (actualLength != sizeof(fIPG)) {
326 		TRACE_ALWAYS("Mismatch of written IPGs data. "
327 			"%d bytes of %d written.\n", actualLength, sizeof(fIPG));
328 	}
329 
330 	uint16 rxcontrol = RXCTL_START | RXCTL_BROADCAST;
331 	result = WriteRXControlRegister(rxcontrol);
332 	if (result != B_OK) {
333 		TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n",
334 			rxcontrol, result);
335 	}
336 
337 	TRACE_RET(result);
338 	return result;
339 }
340 
341 
342 status_t
343 AX88178Device::OnNotify(uint32 actualLength)
344 {
345 	if (actualLength < sizeof(AX88178_Notify)) {
346 		TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n",
347 			actualLength, sizeof(AX88178_Notify));
348 		return B_BAD_DATA;
349 	}
350 
351 	AX88178_Notify *notification = (AX88178_Notify *)fNotifyBuffer;
352 
353 	if (notification->btA1 != 0xa1) {
354 		TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n",
355 			notification->btA1);
356 	}
357 
358 	uint phyIndex = 0;
359 	bool linkIsUp = fHasConnection;
360 	switch(fMII.ActivePHY()) {
361 		case PrimaryPHY:
362 			phyIndex = 1;
363 			linkIsUp = (notification->btBB & LINK_STATE_PPLS)
364 				== LINK_STATE_PPLS;
365 			break;
366 		case SecondaryPHY:
367 			phyIndex = 2;
368 			linkIsUp = (notification->btBB & LINK_STATE_SPLS)
369 				== LINK_STATE_SPLS;
370 			break;
371 		default:
372 		case CurrentPHY:
373 			TRACE_ALWAYS("Error: PHY is not initialized.\n");
374 			return B_NO_INIT;
375 	}
376 
377 	bool linkStateChange = linkIsUp != fHasConnection;
378 	fHasConnection = linkIsUp;
379 
380 	if (linkStateChange) {
381 		TRACE("Link state of PHY%d has been changed to '%s'\n",
382 			phyIndex, fHasConnection ? "up" : "down");
383 	}
384 
385 	if (linkStateChange && fLinkStateChangeSem >= B_OK)
386 		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
387 
388 	return B_OK;
389 }
390 
391 
392 status_t
393 AX88178Device::GetLinkState(ether_link_state *linkState)
394 {
395 	size_t actualLength = 0;
396 	uint16 mediumStatus = 0;
397 	status_t result = gUSBModule->send_request(fDevice,
398 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_MEDIUM_STATUS,
399 		0, 0, sizeof(mediumStatus), &mediumStatus, &actualLength);
400 
401 	if (result != B_OK) {
402 		TRACE_ALWAYS("Error of reading medium status:%#010x.\n", result);
403 		return result;
404 	}
405 
406 	if (actualLength != sizeof(mediumStatus)) {
407 		TRACE_ALWAYS("Mismatch of reading medium status."
408 							"Read %d bytes instead of %d\n",
409 									actualLength, sizeof(mediumStatus));
410 	}
411 
412 	TRACE_FLOW("Medium status is %#04x\n", mediumStatus);
413 
414 	linkState->quality = 1000;
415 
416 	linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0);
417 	linkState->media |= (mediumStatus & MEDIUM_STATE_FD)
418 		? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX;
419 
420 	linkState->speed = (mediumStatus & MEDIUM_STATE_PS_100)
421 		? 100000000 : 10000000;
422 	linkState->speed = (mediumStatus & MEDIUM_STATE_GM)
423 		? 1000000000 : linkState->speed;
424 
425 	TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n",
426 		(linkState->media & IFM_ACTIVE) ? "active" : "inactive",
427 		linkState->speed / 1000000,
428 		(linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
429 	return B_OK;
430 }
431