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
72struct AX88178_PhyIDs {
73	uint8 SecPhyID;
74	uint8 PriPhyID2;
75} _PACKED;
76
77
78// Medium state bits
79enum 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
99enum 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
108enum 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
120enum 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
131enum AX88178_MIISInterfaceStatus {
132	MIIS_IF_STATE_DM	= 0x01,
133	MIIS_IF_STATE_RB	= 0x02
134};
135
136
137// Notification data layout
138struct 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
149enum 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
157const uint16 maxFrameSize = 1536;
158
159
160AX88178Device::AX88178Device(usb_device device, DeviceInfo& deviceInfo)
161	:
162	ASIXDevice(device, deviceInfo)
163{
164	fStatus = InitDevice();
165}
166
167
168status_t
169AX88178Device::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
188status_t
189AX88178Device::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
312status_t
313AX88178Device::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
342status_t
343AX88178Device::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
392status_t
393AX88178Device::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