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
69struct PhyIDs {
70	uint8 PhyID1;
71	uint8 PhyID2;
72} _PACKED;
73
74
75// Medium state bits
76enum 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
84enum 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
93enum 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
104struct 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
117enum AX88172_LinkState {
118	LINK_STATE_PHY1	= 0x01,
119	LINK_STATE_PHY2	= 0x02
120};
121
122
123const uint16 maxFrameSize = 1518;
124
125
126AX88172Device::AX88172Device(usb_device device, DeviceInfo& deviceInfo)
127	:
128	ASIXDevice(device, deviceInfo)
129{
130	fStatus = InitDevice();
131}
132
133
134status_t
135AX88172Device::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
153status_t
154AX88172Device::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
171status_t
172AX88172Device::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
204status_t
205AX88172Device::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
254status_t
255AX88172Device::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