xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_asix/AX88172Device.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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