xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_asix/AX88772Device.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 "AX88772Device.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 AX88772 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_OP_MODE,	//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_PHY_SEL_STATE,	//C021_AA00_0000_0100 Read Software PHY Select Status
56 // WRITE_PHY_SEL,		//4022_AA00_0000_0000 Write Software PHY Select
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 AX88772_PhyIDs {
73 	uint8 SecPhyID;
74 	uint8 PriPhyID2;
75 } _PACKED;
76 
77 
78 // Medium state bits
79 enum AX88772_MediumState {
80 	MEDIUM_STATE_FD		= 0x0002,
81 	MEDIUM_STATE_BIT2	= 0x0004, // must be always set
82 	MEDIUM_STATE_RFC	= 0x0010,
83 	MEDIUM_STATE_TFC	= 0x0020,
84 	MEDIUM_STATE_PF_ON	= 0x0040,
85 	MEDIUM_STATE_PF_OFF	= 0x0000,
86 	MEDIUM_STATE_RE	   	= 0x0100,
87 	MEDIUM_STATE_PS_100	= 0x0200,
88 	MEDIUM_STATE_PS_10 	= 0x0000,
89 	MEDIUM_STATE_SBP1  	= 0x0800,
90 	MEDIUM_STATE_SBP0  	= 0x0000,
91 	MEDIUM_STATE_SM_ON 	= 0x1000
92 };
93 
94 
95 // Monitor Mode bits
96 enum AX88772_MonitorMode {
97 	MONITOR_MODE_MOM	= 0x01,
98 	MONITOR_MODE_RWLU	= 0x02,
99 	MONITOR_MODE_RWMP	= 0x04,
100 	MONITOR_MODE_US 	= 0x10
101 };
102 
103 
104 // General Purpose I/O Register
105 enum AX88772_GPIO {
106 	GPIO_OO_0EN	= 0x01,
107 	GPIO_IO_0	= 0x02,
108 	GPIO_OO_1EN	= 0x04,
109 	GPIO_IO_1	= 0x08,
110 	GPIO_OO_2EN	= 0x10,
111 	GPIO_IO_2	= 0x20,
112 	GPIO_RSE	= 0x80
113 };
114 
115 
116 // Software Reset Register bits
117 enum AX88772_SoftwareReset {
118 	SW_RESET_CLR	= 0x00,
119 	SW_RESET_RR		= 0x01,
120 	SW_RESET_RT		= 0x02,
121 	SW_RESET_PRTE	= 0x04,
122 	SW_RESET_PRL	= 0x08,
123 	SW_RESET_BZ		= 0x10,
124 	SW_RESET_IPRL	= 0x20,
125 	SW_RESET_IPPD	= 0x40
126 };
127 
128 
129 // Software PHY Select Status
130 enum AX88772_SoftwarePHYSelStatus {
131 	SW_PHY_SEL_STATUS_EXT		= 0x00,
132 	SW_PHY_SEL_STATUS_INT		= 0x01,
133 	SW_PHY_SEL_STATUS_ASEL		= 0x02,
134 	SW_PHY_SEL_STATUS_SS_MII	= 0x04,
135 	SW_PHY_SEL_STATUS_SS_RVRS_MII	= 0x08,
136 	SW_PHY_SEL_STATUS_SS_RVRS_GMII	= 0x0C,
137 	SW_PHY_SEL_STATUS_SS_ENB	= 0x10
138 };
139 
140 
141 // Notification data layout
142 struct AX88772_Notify {
143 	uint8  btA1;
144 	uint8  bt01;
145 	uint8  btBB; // AX88772_BBState below
146 	uint8  bt03;
147 	uint16 regCCDD;
148 	uint16 regEEFF;
149 } _PACKED;
150 
151 
152 // Link-State bits
153 enum AX88772_BBState {
154 	LINK_STATE_PPLS		= 0x01,
155 	LINK_STATE_SPLS		= 0x02,
156 	LINK_STATE_FLE		= 0x04,
157 	LINK_STATE_MDINT	= 0x08
158 };
159 
160 // RX Control Register bits (772B)
161 enum ASIX772RXControl {
162 	RXCTL_HDR_TYPE_0	= 0x0000,
163 	RXCTL_HDR_TYPE_1	= 0x0100,
164 	RXCTL_HDR_IPALIGN	= 0x0200,
165 	RXCTL_ADD_CHKSUM	= 0x0400,
166 };
167 
168 // EEPROM Map.
169 enum AX88772B_EEPROM {
170 	EEPROM_772B_NODE_ID	= 0x04,
171 	EEPROM_772B_PHY_PWRCFG	= 0x18
172 };
173 
174 enum AX88772B_MFB {
175 	AX88772B_MFB_2K = 0,
176 	AX88772B_MFB_4K = 1,
177 	AX88772B_MFB_6K = 2,
178 	AX88772B_MFB_8K = 3,
179 	AX88772B_MFB_16K = 4,
180 	AX88772B_MFB_20K = 5,
181 	AX88772B_MFB_24K = 6,
182 	AX88772B_MFB_32K = 7,
183 	AX88772B_MFB_Count = 8
184 };
185 
186 struct _AX88772B_MFB {
187 	size_t ByteCount;
188 	size_t Threshold;
189 	size_t Size;
190 
191 } AX88772B_MFBTable[AX88772B_MFB_Count] = {
192 	{ 0x8000, 0x8001, 2048 },
193 	{ 0x8100, 0x8147, 4096 },
194 	{ 0x8200, 0x81EB, 6144 },
195 	{ 0x8300, 0x83D7, 8192 },
196 	{ 0x8400, 0x851E, 16384 },
197 	{ 0x8500, 0x8666, 20480 },
198 	{ 0x8600, 0x87AE, 24576 },
199 	{ 0x8700, 0x8A3D, 32768 }
200 };
201 
202 const uint16 maxFrameSize = 1536;
203 
204 
205 AX88772Device::AX88772Device(usb_device device, DeviceInfo& deviceInfo)
206 	:
207 	ASIXDevice(device, deviceInfo)
208 {
209 	fStatus = InitDevice();
210 }
211 
212 
213 status_t
214 AX88772Device::InitDevice()
215 {
216 	fFrameSize = maxFrameSize;
217 	fUseTRXHeader = true;
218 
219 	fReadNodeIDRequest = READ_NODEID;
220 
221 	fNotifyBufferLength = sizeof(AX88772_Notify);
222 	fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
223 	if (fNotifyBuffer == NULL) {
224 		TRACE_ALWAYS("Error of allocating memory for notify buffer.\n");
225 		return B_NO_MEMORY;
226 	}
227 
228 	return B_OK;
229 }
230 
231 
232 status_t
233 AX88772Device::ReadMACAddress(ether_address_t *address)
234 {
235 	if (fDeviceInfo.fType != DeviceInfo::AX88772B)
236 		return ASIXDevice::ReadMACAddress(address);
237 
238 	// Auto-loaded default station address from internal ROM is
239 	// 00:00:00:00:00:00 such that an explicit access to EEPROM
240 	// is required to get real station address.
241 	for (size_t i = 0; i < sizeof(ether_address_t) / 2; i++) {
242 		size_t actual_length = 0;
243 		uint16 addr = 0;
244 		status_t result = gUSBModule->send_request(fDevice,
245 			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_SROM,
246 			EEPROM_772B_NODE_ID + i, 0, sizeof(addr), &addr, &actual_length);
247 		if (result != B_OK) {
248 			TRACE_ALWAYS("Error reading MAC[%d] address:%#010x\n", i, result);
249 			return result;
250 		}
251 
252 		address->ebyte[i * 2 + 0] = (uint8)addr;
253 		address->ebyte[i * 2 + 1] = (uint8)(addr >> 8);
254 	}
255 
256 	return B_OK;
257 }
258 
259 
260 status_t
261 AX88772Device::SetupDevice(bool deviceReplugged)
262 {
263 	status_t result = ASIXDevice::SetupDevice(deviceReplugged);
264 	if (result != B_OK) {
265 		return result;
266 	}
267 
268 	result = fMII.Init(fDevice);
269 
270 	switch (fDeviceInfo.fType) {
271 		case DeviceInfo::AX88772A:
272 			result = _SetupAX88772A();
273 			break;
274 		case DeviceInfo::AX88772B:
275 			result = _SetupAX88772B();
276 			break;
277 		default:
278 			result = _SetupAX88772();
279 			break;
280 	}
281 
282 	if (result != B_OK)
283 		return result;
284 
285 	result = fMII.SetupPHY();
286 	if (result != B_OK) {
287 		return result;
288 	}
289 
290 	size_t actualLength = 0;
291 	result = gUSBModule->send_request(fDevice,
292 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_MEDIUM_MODE,
293 		MEDIUM_STATE_FD | MEDIUM_STATE_BIT2 | MEDIUM_STATE_RFC
294 		| MEDIUM_STATE_TFC | MEDIUM_STATE_RE | MEDIUM_STATE_PS_100,
295 		0, 0, 0, &actualLength);
296 
297 	if (result != B_OK) {
298 		TRACE_ALWAYS("Error of setting medium mode: %#010x\n", result);
299 	}
300 
301 	TRACE_RET(result);
302 	return result;
303 }
304 
305 
306 status_t
307 AX88772Device::_SetupAX88772()
308 {
309 	size_t actualLength = 0;
310 	// enable GPIO2 - magic from FreeBSD's if_axe
311 	uint16 GPIOs = GPIO_OO_2EN | GPIO_IO_2 | GPIO_RSE;
312 	status_t result = gUSBModule->send_request(fDevice,
313 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_GPIOS,
314 		GPIOs, 0, 0, 0, &actualLength);
315 
316 	if (result != B_OK) {
317 		TRACE_ALWAYS("Error of wrinting GPIOs: %#010x\n", result);
318 		return result;
319 	}
320 
321 	// select PHY
322 	bool useEmbeddedPHY = fMII.PHYID() == PHYIDEmbedded;
323 	uint16 selectPHY = useEmbeddedPHY
324 		? SW_PHY_SEL_STATUS_INT : SW_PHY_SEL_STATUS_EXT;
325 
326 	result = gUSBModule->send_request(fDevice,
327 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
328 		WRITE_PHY_SEL, selectPHY, 0, 0, 0, &actualLength);
329 	snooze(10000);
330 
331 	TRACE("Selecting %s PHY[%#02x].\n",
332 		useEmbeddedPHY ? "embedded" : "external", selectPHY);
333 
334 	if (result != B_OK) {
335 		TRACE_ALWAYS("Error of selecting PHY:%#010x\n", result);
336 		return result;
337 	}
338 
339 	struct SWReset {
340 		uint16 		reset;
341 		bigtime_t	delay;
342 	} resetCommands[] = {
343 	// EMBEDDED PHY
344 		// power down and reset state, pin reset state
345 		{ SW_RESET_CLR, 60000 },
346 		// power down/reset state, pin operating state
347 		{ SW_RESET_PRL | SW_RESET_IPPD, 150000 },
348 		// power up, reset
349 		{ SW_RESET_PRL, 0 },
350 		// power up, operating
351 		{ SW_RESET_PRL | SW_RESET_IPRL, 0 },
352 	// EXTERNAL PHY
353 		// power down/reset state, pin operating state
354 		{ SW_RESET_PRL | SW_RESET_IPPD, 0 }
355 	};
356 
357 	size_t from = useEmbeddedPHY ? 0 : 4;
358 	size_t to   = useEmbeddedPHY ? 3 : 4;
359 
360 	for (size_t i = from; i <= to; i++) {
361 		result = gUSBModule->send_request(fDevice,
362 			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SOFT_RESET,
363 			resetCommands[i].reset, 0, 0, 0, &actualLength);
364 
365 		snooze(resetCommands[i].delay);
366 
367 		if (result != B_OK) {
368 			TRACE_ALWAYS("Error of SW reset command %d:[%#04x]: %#010x\n",
369 				i, resetCommands[i].reset, result);
370 			return result;
371 		}
372 	}
373 
374 	snooze(150000);
375 
376 	return B_OK;
377 }
378 
379 
380 status_t
381 AX88772Device::_WakeupPHY()
382 {
383 	// select PHY
384 	bool useEmbeddedPHY = fMII.PHYID() == PHYIDEmbedded;
385 	uint16 selectPHY = useEmbeddedPHY
386 		? SW_PHY_SEL_STATUS_INT : SW_PHY_SEL_STATUS_EXT;
387 
388 	selectPHY |= SW_PHY_SEL_STATUS_SS_MII | SW_PHY_SEL_STATUS_SS_ENB;
389 
390 	size_t actualLength = 0;
391 	status_t result = gUSBModule->send_request(fDevice,
392 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_PHY_SEL,
393 		selectPHY, 0, 0, 0, &actualLength);
394 	snooze(31000);
395 
396 	TRACE("Selecting %s PHY[%#02x].\n",
397 		useEmbeddedPHY ? "embedded" : "external", selectPHY);
398 
399 	if (result != B_OK) {
400 		TRACE_ALWAYS("Error of selecting PHY:%#010x\n", result);
401 		return result;
402 	}
403 
404 	struct SWReset {
405 		uint16 		reset;
406 		bigtime_t	delay;
407 	} resetCommands[] = {
408 		{ SW_RESET_IPRL | SW_RESET_IPPD, 250000 },
409 		{ SW_RESET_IPRL, 1000000 },
410 		{ SW_RESET_CLR, 31000 },
411 		{ SW_RESET_IPRL, 31000 }
412 	};
413 
414 	for (size_t i = 0; i < B_COUNT_OF(resetCommands); i++) {
415 		result = gUSBModule->send_request(fDevice,
416 			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SOFT_RESET,
417 			resetCommands[i].reset, 0, 0, 0, &actualLength);
418 
419 		snooze(resetCommands[i].delay);
420 
421 		if (result != B_OK) {
422 			TRACE_ALWAYS("Error of SW reset command %d:[%#04x]: %#010x\n",
423 				i, resetCommands[i].reset, result);
424 			return result;
425 		}
426 	}
427 
428 	return B_OK;
429 }
430 
431 
432 status_t
433 AX88772Device::_SetupAX88772A()
434 {
435 	// Reload EEPROM
436 	size_t actualLength = 0;
437 	status_t result = gUSBModule->send_request(fDevice,
438 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_GPIOS,
439 		GPIO_RSE, 0, 0, 0, &actualLength);
440 
441 	if (result != B_OK) {
442 		TRACE_ALWAYS("Error of reloading EEPROM: %#010x\n", result);
443 		return result;
444 	}
445 
446 	result = _WakeupPHY();
447 	if (result != B_OK)
448 		return result;
449 
450 	fIPG[0] = 0x15;
451 	fIPG[1] = 0x16;
452 	fIPG[2] = 0x1A;
453 
454 	return B_OK;
455 }
456 
457 
458 status_t
459 AX88772Device::_SetupAX88772B()
460 {
461 	// Reload EEPROM
462 	size_t actualLength = 0;
463 	status_t result = gUSBModule->send_request(fDevice,
464 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_GPIOS,
465 		GPIO_RSE, 0, 0, 0, &actualLength);
466 
467 	if (result != B_OK) {
468 		TRACE_ALWAYS("Error of reloading EEPROM: %#010x\n", result);
469 		return result;
470 	}
471 
472 	result = _WakeupPHY();
473 	if (result != B_OK)
474 		return result;
475 
476 	fIPG[0] = 0x15;
477 	fIPG[1] = 0x16;
478 	fIPG[2] = 0x1A;
479 
480 	return B_OK;
481 }
482 
483 
484 status_t
485 AX88772Device::StartDevice()
486 {
487 	size_t actualLength = 0;
488 	status_t result = gUSBModule->send_request(fDevice,
489 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_IPGS,
490 		0, 0, sizeof(fIPG), fIPG, &actualLength);
491 
492 	if (result != B_OK) {
493 		TRACE_ALWAYS("Error of writing IPGs:%#010x\n", result);
494 		return result;
495 	}
496 
497 	if (actualLength != sizeof(fIPG)) {
498 		TRACE_ALWAYS("Mismatch of written IPGs data. "
499 			"%d bytes of %d written.\n", actualLength, sizeof(fIPG));
500 
501 	}
502 
503 	uint16 rxcontrol = 0;
504 
505 	// AX88772B uses different maximum frame burst configuration.
506 	if (fDeviceInfo.fType == DeviceInfo::AX88772B) {
507 		result = gUSBModule->send_request(fDevice,
508 			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_RXCONTROL_CFG,
509 			AX88772B_MFBTable[AX88772B_MFB_2K].ByteCount,
510 			AX88772B_MFBTable[AX88772B_MFB_2K].Threshold, 0, 0, &actualLength);
511 
512 		if (result != B_OK) {
513 			TRACE_ALWAYS("Error of writing frame burst:%#010x\n", result);
514 			return result;
515 		}
516 		rxcontrol = RXCTL_HDR_TYPE_1;
517 	} else {
518 		// TODO: FreeBSD documents this to speed up xfers, I don't
519 		// have the hardware to test however.
520 		// rxcontrol = RXCTL_USB_MFB_MAX;
521 	}
522 
523 	rxcontrol |= RXCTL_START | RXCTL_BROADCAST;
524 	result = WriteRXControlRegister(rxcontrol);
525 	if (result != B_OK) {
526 		TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n",
527 			rxcontrol, result);
528 	}
529 
530 	TRACE_RET(result);
531 	return result;
532 }
533 
534 
535 status_t
536 AX88772Device::OnNotify(uint32 actualLength)
537 {
538 	if (actualLength < sizeof(AX88772_Notify)) {
539 		TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n",
540 			actualLength, sizeof(AX88772_Notify));
541 		return B_BAD_DATA;
542 	}
543 
544 	AX88772_Notify *notification = (AX88772_Notify *)fNotifyBuffer;
545 
546 	if (notification->btA1 != 0xa1) {
547 		TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n",
548 			notification->btA1);
549 	}
550 
551 	uint phyIndex = 0;
552 	bool linkIsUp = fHasConnection;
553 	switch(fMII.ActivePHY()) {
554 		case PrimaryPHY:
555 			phyIndex = 1;
556 			linkIsUp = (notification->btBB & LINK_STATE_PPLS)
557 				== LINK_STATE_PPLS;
558 			break;
559 		case SecondaryPHY:
560 			phyIndex = 2;
561 			linkIsUp = (notification->btBB & LINK_STATE_SPLS)
562 				== LINK_STATE_SPLS;
563 			break;
564 		default:
565 		case CurrentPHY:
566 			TRACE_ALWAYS("Error: PHY is not initialized.\n");
567 			return B_NO_INIT;
568 	}
569 
570 	bool linkStateChange = linkIsUp != fHasConnection;
571 	fHasConnection = linkIsUp;
572 
573 	if (linkStateChange) {
574 		TRACE("Link state of PHY%d has been changed to '%s'\n",
575 			phyIndex, fHasConnection ? "up" : "down");
576 	}
577 
578 	if (linkStateChange && fLinkStateChangeSem >= B_OK)
579 		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
580 
581 	return B_OK;
582 }
583 
584 
585 status_t
586 AX88772Device::GetLinkState(ether_link_state *linkState)
587 {
588 	size_t actualLength = 0;
589 	uint16 mediumStatus = 0;
590 	status_t result = gUSBModule->send_request(fDevice,
591 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_MEDIUM_STATUS,
592 		0, 0, sizeof(mediumStatus), &mediumStatus, &actualLength);
593 
594 	if (result != B_OK) {
595 		TRACE_ALWAYS("Error of reading medium status:%#010x.\n", result);
596 		return result;
597 	}
598 
599 	if (actualLength != sizeof(mediumStatus)) {
600 		TRACE_ALWAYS("Mismatch of reading medium status."
601 			"Read %d bytes instead of %d\n", actualLength,
602 			sizeof(mediumStatus));
603 	}
604 
605 	TRACE_FLOW("Medium status is %#04x\n", mediumStatus);
606 
607 	linkState->quality = 1000;
608 
609 	linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0);
610 	linkState->media |= (mediumStatus & MEDIUM_STATE_FD)
611 		? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX;
612 
613 	linkState->speed = (mediumStatus & MEDIUM_STATE_PS_100)
614 		? 100000000 : 10000000;
615 
616 	TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n",
617 		(linkState->media & IFM_ACTIVE) ? "active" : "inactive",
618 		linkState->speed / 1000000,
619 		(linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
620 	return B_OK;
621 }
622