xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_davicom/DavicomDevice.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  *	Davicom DM9601 USB 1.1 Ethernet Driver.
3  *	Copyright (c) 2008, 2011 Siarzhuk Zharski <imker@gmx.li>
4  *	Copyright (c) 2009 Adrien Destugues <pulkomandy@gmail.com>
5  *	Distributed under the terms of the MIT license.
6  *
7  *	Heavily based on code of the
8  *	Driver for USB Ethernet Control Model devices
9  *	Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
10  *	Distributed under the terms of the MIT license.
11  */
12 
13 
14 #include "DavicomDevice.h"
15 
16 #include <stdio.h>
17 #include <net/if_media.h>
18 
19 #include "Driver.h"
20 #include "Settings.h"
21 
22 
23 const int kFrameSize = 1522;
24 
25 enum VendorRequests {
26 	ReqReadRegister			= 0,
27 	ReqWriteRegister		= 1,
28 	ReqWriteRegisterByte	= 3,
29 };
30 
31 
32 enum DM9601Registers {
33 	RegNCR	= 0x00,	// Network Control Register
34 		NCRExtPHY	= 0x80,	// Select External PHY
35 		NCRFullDX	= 0x08,	// Full duplex
36 		NCRLoopback	= 0x06,	// Internal PHY analog loopback
37 
38 	RegNSR	= 0x01,	// Network Status Register
39 		NSRSpeed10	= 0x80,	// 0 = 100MBps, 1 = 10MBps (internal PHY)
40 		NSRLinkUp	= 0x40,	// 1 = link up (internal PHY)
41 		NSRTXFull	= 0x10,	// TX FIFO full
42 		NSRRXOver	= 0x08,	// RX FIFO overflow
43 
44 	RegRCR	= 0x05,	// RX Control Register
45 		RCRDiscardLong	= 0x20,	// Discard long packet (over 1522 bytes)
46 		RCRDiscardCRC	= 0x10,	// Discard CRC error packet
47 		RCRAllMulticast	= 0x08,	// Pass all multicast
48 		RCRPromiscuous	= 0x02,	// Promiscuous
49 		RCRRXEnable		= 0x01,	// RX enable
50 
51 	RegEPCR	= 0x0b,	// EEPROM & PHY Control Register
52 		EPCROpSelect	= 0x08,	// EEPROM or PHY Operation Select
53 		EPCRRegRead		= 0x04,	// EEPROM or PHY Register Read Command
54 		EPCRRegWrite	= 0x02,	// EEPROM or PHY Register Write Command
55 
56 	RegEPAR	= 0x0c,	// EEPROM & PHY Address Register
57 		EPARIntPHY		= 0x40,	// [7:6] force to 01 if Internal PHY is selected
58 		EPARMask		= 0x1f,	// mask [0:5]
59 
60 	RegEPDRL = 0x0d, // EEPROM & PHY Low Byte Data Register
61 
62 	RegEPDRH = 0x0e, // EEPROM & PHY Low Byte Data Register
63 
64 	RegPAR	= 0x10,	// [0x10 - 0x15] Physical Address Register
65 
66 	RegMAR	= 0x16,	// [0x16 - 0x1d] Multicast Address Register
67 
68 	RegGPCR	= 0x1E,	// General Purpose Control Register
69 		GPCRPowerDown	= 0x01,	// [0:6] Define in/out direction of GPCR
70 								// GPIO0 - is output for Power Down function
71 
72 	RegGPR	= 0x1F,	// General Purpose Register
73 		GPRPowerDownInPHY = 0x01,	// Power down Internal PHY
74 
75 	RegUSBC	= 0xf4, // USB Control Register
76 		USBCIntAck		= 0x20,	// ACK with 8-bytes of data on interrupt EP
77 		USBCIntNAck		= 0x10,	// Supress ACK on interrupt EP
78 
79 };
80 
81 
82 enum MIIRegisters {
83 	RegBMCR	= 0x00,
84 		BMCRIsolate	= 0x0400,
85 		BMCRReset	= 0x8000,
86 
87 	RegBMSR	= 0x01,
88 	RegPHYID1	= 0x02,
89 	RegPHYID2	= 0x03,
90 };
91 
92 #define MII_OUI(id1, id2)       (((id1) << 6) | ((id2) >> 10))
93 #define MII_MODEL(id2)          (((id2) & 0x03f0) >> 4)
94 #define MII_REV(id2)             ((id2) & 0x000f)
95 
96 
97 DavicomDevice::DavicomDevice(usb_device device, DeviceInfo& deviceInfo)
98 	:	fDevice(device),
99 		fStatus(B_ERROR),
100 		fOpen(false),
101 		fRemoved(false),
102 		fHasConnection(false),
103 		fTXBufferFull(false),
104 		fNonBlocking(false),
105 		fInsideNotify(0),
106 		fNotifyEndpoint(0),
107 		fReadEndpoint(0),
108 		fWriteEndpoint(0),
109 		fMaxTXPacketSize(0),
110 		fActualLengthRead(0),
111 		fActualLengthWrite(0),
112 		fStatusRead(0),
113 		fStatusWrite(0),
114 		fNotifyReadSem(-1),
115 		fNotifyWriteSem(-1),
116 		fLinkStateChangeSem(-1),
117 		fNotifyData(NULL)
118 {
119 	fDeviceInfo = deviceInfo;
120 
121 	memset(&fMACAddress, 0, sizeof(fMACAddress));
122 
123 	fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
124 	if (fNotifyReadSem < B_OK) {
125 		TRACE_ALWAYS("Error of creating read notify semaphore:%#010x\n",
126 															fNotifyReadSem);
127 		return;
128 	}
129 
130 	fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
131 	if (fNotifyWriteSem < B_OK) {
132 		TRACE_ALWAYS("Error of creating write notify semaphore:%#010x\n",
133 															fNotifyWriteSem);
134 		return;
135 	}
136 
137 	fNotifyData = new DM9601NotifyData();
138 	if (fNotifyData == NULL) {
139 		TRACE_ALWAYS("Error allocating notify buffer\n");
140 		return;
141 	}
142 
143 	if (_SetupEndpoints() != B_OK) {
144 		return;
145 	}
146 
147 	_InitMII();
148 
149 	fStatus = B_OK;
150 	TRACE("Created!\n");
151 }
152 
153 
154 DavicomDevice::~DavicomDevice()
155 {
156 	if (fNotifyReadSem >= B_OK)
157 		delete_sem(fNotifyReadSem);
158 	if (fNotifyWriteSem >= B_OK)
159 		delete_sem(fNotifyWriteSem);
160 
161 	if (!fRemoved) // ???
162 		gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
163 
164 	delete fNotifyData;
165 	TRACE("Deleted!\n");
166 }
167 
168 
169 status_t
170 DavicomDevice::Open(uint32 flags)
171 {
172 	if (fOpen)
173 		return B_BUSY;
174 	if (fRemoved)
175 		return B_ERROR;
176 
177 	status_t result = _StartDevice();
178 	if (result != B_OK) {
179 		return result;
180 	}
181 
182 	// setup state notifications
183 	result = gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyData,
184 		sizeof(DM9601NotifyData), _NotifyCallback, this);
185 	if (result != B_OK) {
186 		TRACE_ALWAYS("Error of requesting notify interrupt:%#010x\n", result);
187 		return result;
188 	}
189 
190 	result = _EnableInterrupts(true);
191 
192 	fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK;
193 	fOpen = true;
194 	TRACE("Opened: %#010x!\n", result);
195 	return result;
196 }
197 
198 
199 status_t
200 DavicomDevice::Close()
201 {
202 	if (fRemoved) {
203 		fOpen = false;
204 		return B_OK;
205 	}
206 
207 	_EnableInterrupts(false);
208 
209 	// wait until possible notification handling finished...
210 	while (atomic_add(&fInsideNotify, 0) != 0)
211 		snooze(100);
212 	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
213 	gUSBModule->cancel_queued_transfers(fReadEndpoint);
214 	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
215 
216 	fOpen = false;
217 
218 	status_t result = _StopDevice();
219 	TRACE("Closed: %#010x!\n", result);
220 	return result;
221 }
222 
223 
224 status_t
225 DavicomDevice::Free()
226 {
227 	TRACE("Freed!\n");
228 	return B_OK;
229 }
230 
231 
232 status_t
233 DavicomDevice::Read(uint8 *buffer, size_t *numBytes)
234 {
235 	size_t numBytesToRead = *numBytes;
236 	*numBytes = 0;
237 
238 	if (fRemoved) {
239 		TRACE_ALWAYS("Error of receiving %d bytes from removed device.\n",
240 			numBytesToRead);
241 		return B_DEVICE_NOT_FOUND;
242 	}
243 
244 	TRACE_RX("Request %d bytes.\n", numBytesToRead);
245 
246 	struct _RXHeader {
247 		uint	FOE	:1;
248 		uint	CE	:1;
249 		uint	LE	:1;
250 		uint	PLE	:1;
251 		uint	RWTO:1;
252 		uint	LCS	:1;
253 		uint	MF	:1;
254 		uint	RF	:1;
255 		uint	countLow	:8;
256 		uint	countHigh	:8;
257 
258 		uint8	Errors() { return 0xbf & *(uint8*)this; }
259 	} __attribute__((__packed__));
260 
261 	_RXHeader header = { 0 };
262 
263 	iovec rxData[] = {
264 		{ &header, sizeof(header) },
265 		{ buffer,  numBytesToRead }
266 	};
267 
268 	status_t result = gUSBModule->queue_bulk_v(fReadEndpoint,
269 		rxData, 2, _ReadCallback, this);
270 	if (result != B_OK) {
271 		TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result);
272 		return result;
273 	}
274 
275 	uint32 flags = B_CAN_INTERRUPT | (fNonBlocking ? B_TIMEOUT : 0);
276 	result = acquire_sem_etc(fNotifyReadSem, 1, flags, 0);
277 	if (result < B_OK) {
278 		TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result);
279 		return result;
280 	}
281 
282 	if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) {
283 		TRACE_ALWAYS("Device status error:%#010x\n", fStatusRead);
284 		return fStatusRead;
285 	}
286 
287 	if (fActualLengthRead < sizeof(_RXHeader)) {
288 		TRACE_ALWAYS("Error: no place for RXHeader: only %d of %d bytes.\n",
289 			fActualLengthRead, sizeof(_RXHeader));
290 		return B_ERROR;
291 	}
292 
293 	if (header.Errors() != 0) {
294 		TRACE_ALWAYS("RX header errors %#04x detected!\n", header.Errors());
295 	}
296 
297 	TRACE_STATS("FOE:%d CE:%d LE:%d PLE:%d rwTO:%d LCS:%d MF:%d RF:%d\n",
298 			header.FOE, header.CE, header.LE, header.PLE,
299 			header.RWTO, header.LCS, header.MF, header.RF);
300 
301 	*numBytes = header.countLow | ( header.countHigh << 8 );
302 
303 	if (fActualLengthRead - sizeof(_RXHeader) > *numBytes) {
304 		TRACE_ALWAYS("MISMATCH of the frame length: hdr %d; received:%d\n",
305 			*numBytes, fActualLengthRead - sizeof(_RXHeader));
306 	}
307 
308 	TRACE_RX("Read %d bytes.\n", *numBytes);
309 	return B_OK;
310 }
311 
312 
313 status_t
314 DavicomDevice::Write(const uint8 *buffer, size_t *numBytes)
315 {
316 	size_t numBytesToWrite = *numBytes;
317 	*numBytes = 0;
318 
319 	if (fRemoved) {
320 		TRACE_ALWAYS("Error of writing %d bytes to removed device.\n",
321 			numBytesToWrite);
322 		return B_DEVICE_NOT_FOUND;
323 	}
324 
325 	if (!fHasConnection) {
326 		TRACE_ALWAYS("Error of writing %d bytes to device while down.\n",
327 			numBytesToWrite);
328 		return B_ERROR;
329 	}
330 
331 	if (fTXBufferFull) {
332 		TRACE_ALWAYS("Error of writing %d bytes to device: TX buffer full.\n",
333 			numBytesToWrite);
334 		return B_ERROR;
335 	}
336 
337 	TRACE_TX("Write %d bytes.\n", numBytesToWrite);
338 
339 	// additional padding byte must be transmitted in case data size
340 	// to be send is multiple of pipe's max packet size
341 	uint16 length = numBytesToWrite;
342 	size_t count = 2;
343 	if (((numBytesToWrite + 2) % fMaxTXPacketSize) == 0) {
344 		length++;
345 		count++;
346 	}
347 
348 	struct _TXHeader {
349 		uint8	countLow;
350 		uint8	countHigh;
351 	} __attribute__((__packed__));
352 
353 	_TXHeader header = { (uint8)(length & 0xff),
354 		(uint8)((length >> 8) & 0xff) };
355 
356 	uint8 padding = 0;
357 
358 	iovec txData[] = {
359 		{ &header, sizeof(_TXHeader) },
360 		{ (uint8*)buffer, numBytesToWrite },
361 		{ &padding, 1 }
362 	};
363 
364 	status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint,
365 		txData, count, _WriteCallback, this);
366 	if (result != B_OK) {
367 		TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result);
368 		return result;
369 	}
370 
371 	result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0);
372 
373 	if (result < B_OK) {
374 		TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result);
375 		return result;
376 	}
377 
378 	if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) {
379 		TRACE_ALWAYS("Device status error:%#010x\n", fStatusWrite);
380 		return fStatusWrite;
381 	}
382 
383 	*numBytes = fActualLengthWrite - sizeof(_TXHeader);
384 
385 	TRACE_TX("Written %d bytes.\n", *numBytes);
386 	return B_OK;
387 }
388 
389 
390 status_t
391 DavicomDevice::Control(uint32 op, void *buffer, size_t length)
392 {
393 	switch (op) {
394 		case ETHER_INIT:
395 			return B_OK;
396 
397 		case ETHER_GETADDR:
398 			memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
399 			return B_OK;
400 
401 		case ETHER_GETFRAMESIZE:
402 			*(uint32 *)buffer = kFrameSize;
403 			return B_OK;
404 
405 		case ETHER_NONBLOCK:
406 			TRACE("ETHER_NONBLOCK\n");
407 			fNonBlocking = *((uint8*)buffer);
408 			return B_OK;
409 
410 		case ETHER_SETPROMISC:
411 			TRACE("ETHER_SETPROMISC\n");
412 			return _SetPromiscuousMode(*((uint8*)buffer));
413 
414 		case ETHER_ADDMULTI:
415 			TRACE("ETHER_ADDMULTI\n");
416 			return _ModifyMulticastTable(true, (ether_address_t*)buffer);
417 
418 		case ETHER_REMMULTI:
419 			TRACE("ETHER_REMMULTI\n");
420 			return _ModifyMulticastTable(false, (ether_address_t*)buffer);
421 
422 		case ETHER_SET_LINK_STATE_SEM:
423 			fLinkStateChangeSem = *(sem_id *)buffer;
424 			return B_OK;
425 
426 		case ETHER_GET_LINK_STATE:
427 			return _GetLinkState((ether_link_state *)buffer);
428 
429 		default:
430 			TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op);
431 	}
432 
433 	return B_DEV_INVALID_IOCTL;
434 }
435 
436 
437 void
438 DavicomDevice::Removed()
439 {
440 	fRemoved = true;
441 	fHasConnection = false;
442 
443 	// the notify hook is different from the read and write hooks as it does
444 	// itself schedule traffic (while the other hooks only release a semaphore
445 	// to notify another thread which in turn safly checks for the removed
446 	// case) - so we must ensure that we are not inside the notify hook anymore
447 	// before returning, as we would otherwise violate the promise not to use
448 	// any of the pipes after returning from the removed hook
449 	while (atomic_add(&fInsideNotify, 0) != 0)
450 		snooze(100);
451 
452 	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
453 	gUSBModule->cancel_queued_transfers(fReadEndpoint);
454 	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
455 
456 	if (fLinkStateChangeSem >= B_OK)
457 		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
458 }
459 
460 
461 status_t
462 DavicomDevice::SetupDevice(bool deviceReplugged)
463 {
464 	ether_address address;
465 	status_t result = _ReadMACAddress(&address);
466 	if (result != B_OK) {
467 		TRACE_ALWAYS("Error reading MAC address:%#010x\n", result);
468 		return result;
469 	}
470 
471 	TRACE("MAC address is:%02x:%02x:%02x:%02x:%02x:%02x\n",
472 				address.ebyte[0], address.ebyte[1], address.ebyte[2],
473 				address.ebyte[3], address.ebyte[4], address.ebyte[5]);
474 
475 	if (deviceReplugged) {
476 		// this might be the same device that was replugged - read the MAC
477 		// address (which should be at the same index) to make sure
478 		if (memcmp(&address, &fMACAddress, sizeof(address)) != 0) {
479 			TRACE_ALWAYS("Cannot replace device with MAC address:"
480 				"%02x:%02x:%02x:%02x:%02x:%02x\n",
481 				fMACAddress.ebyte[0], fMACAddress.ebyte[1],
482 				fMACAddress.ebyte[2], fMACAddress.ebyte[3],
483 				fMACAddress.ebyte[4], fMACAddress.ebyte[5]);
484 			return B_BAD_VALUE; // is not the same
485 		}
486 	} else
487 		fMACAddress = address;
488 
489 	return B_OK;
490 }
491 
492 
493 status_t
494 DavicomDevice::CompareAndReattach(usb_device device)
495 {
496 	const usb_device_descriptor *deviceDescriptor
497 		= gUSBModule->get_device_descriptor(device);
498 
499 	if (deviceDescriptor == NULL) {
500 		TRACE_ALWAYS("Error getting USB device descriptor.\n");
501 		return B_ERROR;
502 	}
503 
504 	if (deviceDescriptor->vendor_id != fDeviceInfo.VendorId()
505 		&& deviceDescriptor->product_id != fDeviceInfo.ProductId()) {
506 		// this certainly isn't the same device
507 		return B_BAD_VALUE;
508 	}
509 
510 	// this is the same device that was replugged - clear the removed state,
511 	// re-setup the endpoints and transfers and open the device if it was
512 	// previously opened
513 	fDevice = device;
514 	fRemoved = false;
515 	status_t result = _SetupEndpoints();
516 	if (result != B_OK) {
517 		fRemoved = true;
518 		return result;
519 	}
520 
521 	// we need to setup hardware on device replug
522 	result = SetupDevice(true);
523 	if (result != B_OK) {
524 		return result;
525 	}
526 
527 	if (fOpen) {
528 		fOpen = false;
529 		result = Open(fNonBlocking ? O_NONBLOCK : 0);
530 	}
531 
532 	return result;
533 }
534 
535 
536 status_t
537 DavicomDevice::_SetupEndpoints()
538 {
539 	const usb_configuration_info *config
540 		= gUSBModule->get_nth_configuration(fDevice, 0);
541 
542 	if (config == NULL) {
543 		TRACE_ALWAYS("Error of getting USB device configuration.\n");
544 		return B_ERROR;
545 	}
546 
547 	if (config->interface_count <= 0) {
548 		TRACE_ALWAYS("Error:no interfaces found in USB device configuration\n");
549 		return B_ERROR;
550 	}
551 
552 	usb_interface_info *interface = config->interface[0].active;
553 	if (interface == 0) {
554 		TRACE_ALWAYS("Error:invalid active interface in "
555 												"USB device configuration\n");
556 		return B_ERROR;
557 	}
558 
559 	int notifyEndpoint = -1;
560 	int readEndpoint   = -1;
561 	int writeEndpoint  = -1;
562 
563 	for (size_t ep = 0; ep < interface->endpoint_count; ep++) {
564 		usb_endpoint_descriptor *epd = interface->endpoint[ep].descr;
565 		if ((epd->attributes & USB_ENDPOINT_ATTR_MASK)
566 				== USB_ENDPOINT_ATTR_INTERRUPT)
567 		{
568 			notifyEndpoint = ep;
569 			continue;
570 		}
571 
572 		if ((epd->attributes & USB_ENDPOINT_ATTR_MASK)
573 				!= USB_ENDPOINT_ATTR_BULK)
574 		{
575 			TRACE_ALWAYS("Error: USB endpoint type %#04x is unknown.\n",
576 					epd->attributes);
577 			continue;
578 		}
579 
580 		if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK)
581 				== USB_ENDPOINT_ADDR_DIR_IN)
582 		{
583 			readEndpoint = ep;
584 			continue;
585 		}
586 
587 		if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK)
588 				== USB_ENDPOINT_ADDR_DIR_OUT)
589 		{
590 			writeEndpoint = ep;
591 			continue;
592 		}
593 	}
594 
595 	if (notifyEndpoint == -1 || readEndpoint == -1 || writeEndpoint == -1) {
596 		TRACE_ALWAYS("Error: not all USB endpoints were found: notify:%d; "
597 			"read:%d; write:%d\n", notifyEndpoint, readEndpoint, writeEndpoint);
598 		return B_ERROR;
599 	}
600 
601 	gUSBModule->set_configuration(fDevice, config);
602 
603 	fNotifyEndpoint = interface->endpoint[notifyEndpoint].handle;
604 	fReadEndpoint   = interface->endpoint[readEndpoint  ].handle;
605 	fWriteEndpoint  = interface->endpoint[writeEndpoint ].handle;
606 	fMaxTXPacketSize = interface->endpoint[writeEndpoint].descr->max_packet_size;
607 
608 	return B_OK;
609 }
610 
611 
612 status_t
613 DavicomDevice::_ReadMACAddress(ether_address_t *address)
614 {
615 	status_t result = _ReadRegister(RegPAR,
616 							sizeof(ether_address), (uint8*)address);
617 	if (result != B_OK) {
618 		TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result);
619 		return result;
620 	}
621 
622 	return B_OK;
623 }
624 
625 
626 status_t
627 DavicomDevice::_StartDevice()
628 {
629 	uint8 control = 0;
630 
631 	// disable loopback
632 	status_t result = _ReadRegister(RegNCR, 1, &control);
633 	if (result != B_OK) {
634 		TRACE_ALWAYS("Error reading NCR: %#010x.\n", result);
635 		return result;
636 	}
637 
638 	if (control & NCRExtPHY)
639 		TRACE_ALWAYS("Device uses external PHY\n");
640 
641 	control &= ~NCRLoopback;
642 	result = _Write1Register(RegNCR, control);
643 	if (result != B_OK) {
644 		TRACE_ALWAYS("Error writing %#02X to NCR: %#010x.\n", control, result);
645 		return result;
646 	}
647 
648 	// Initialize RX control register, enable RX and activate multicast
649 	result = _ReadRegister(RegRCR, 1, &control);
650 	if (result != B_OK) {
651 		TRACE_ALWAYS("Error reading RCR: %#010x.\n", result);
652 		return result;
653 	}
654 
655 	control &= ~RCRPromiscuous;
656 	control |= RCRDiscardLong | RCRDiscardCRC | RCRRXEnable | RCRAllMulticast;
657 	result = _Write1Register(RegRCR, control);
658 	if (result != B_OK) {
659 		TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result);
660 		return result;
661 	}
662 
663 	// clear POWER_DOWN state of internal PHY
664 	result = _ReadRegister(RegGPCR, 1, &control);
665 	if (result != B_OK) {
666 		TRACE_ALWAYS("Error reading GPCR: %#010x.\n", result);
667 		return result;
668 	}
669 
670 	control |= GPCRPowerDown;
671 	result = _Write1Register(RegGPCR, control);
672 	if (result != B_OK) {
673 		TRACE_ALWAYS("Error writing %#02X to GPCR: %#010x.\n", control, result);
674 		return result;
675 	}
676 
677 	result = _ReadRegister(RegGPR, 1, &control);
678 	if (result != B_OK) {
679 		TRACE_ALWAYS("Error reading GPR: %#010x.\n", result);
680 		return result;
681 	}
682 
683 	control &= ~GPRPowerDownInPHY;
684 	result = _Write1Register(RegGPR, control);
685 	if (result != B_OK) {
686 		TRACE_ALWAYS("Error writing %#02X to GPR: %#010x.\n", control, result);
687 		return result;
688 	}
689 
690 	return B_OK;
691 }
692 
693 
694 status_t
695 DavicomDevice::_StopDevice()
696 {
697 	uint8 control = 0;
698 
699 	// disable RX
700 	status_t result = _ReadRegister(RegRCR, 1, &control);
701 	if (result != B_OK) {
702 		TRACE_ALWAYS("Error reading RCR: %#010x.\n", result);
703 		return result;
704 	}
705 
706 	control &= ~RCRRXEnable;
707 	result = _Write1Register(RegRCR, control);
708 	if (result != B_OK)
709 		TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result);
710 
711 	return result;
712 }
713 
714 
715 status_t
716 DavicomDevice::_SetPromiscuousMode(bool on)
717 {
718 	uint8 control = 0;
719 
720 	status_t result = _ReadRegister(RegRCR, 1, &control);
721 	if (result != B_OK) {
722 		TRACE_ALWAYS("Error reading RCR: %#010x.\n", result);
723 		return result;
724 	}
725 
726 	if (on)
727 		control |= RCRPromiscuous;
728 	else
729 		control &= ~RCRPromiscuous;
730 
731 	result = _Write1Register(RegRCR, control);
732 	if (result != B_OK)
733 		TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result);
734 
735 	return result;
736 }
737 
738 
739 uint32
740 DavicomDevice::_EthernetCRC32(const uint8* buffer, size_t length)
741 {
742 	uint32 result = 0xffffffff;
743 	for (size_t i = 0; i < length; i++) {
744 		uint8 data = *buffer++;
745 		for (int bit = 0; bit < 8; bit++, data >>= 1) {
746 			uint32 carry = ((result & 0x80000000) ? 1 : 0) ^ (data & 0x01);
747 			result <<= 1;
748 			if (carry != 0)
749 				result = (result ^ 0x04c11db6) | carry;
750 		}
751 	}
752 	return result;
753 }
754 
755 
756 status_t
757 DavicomDevice::_ModifyMulticastTable(bool join, ether_address_t *group)
758 {
759 	char groupName[6 * 3 + 1] = { 0 };
760 	sprintf(groupName, "%02x:%02x:%02x:%02x:%02x:%02x",
761 		group->ebyte[0], group->ebyte[1], group->ebyte[2],
762 		group->ebyte[3], group->ebyte[4], group->ebyte[5]);
763 	TRACE("%s multicast group %s\n", join ? "Joining" : "Leaving", groupName);
764 
765 	uint32 hash = _EthernetCRC32(group->ebyte, 6);
766 	bool isInTable = fMulticastHashes.Find(hash) != fMulticastHashes.End();
767 
768 	if (isInTable && join)
769 		return B_OK; // already listed - nothing to do
770 
771 	if (!isInTable && !join) {
772 		TRACE_ALWAYS("Cannot leave unlisted multicast group %s!\n", groupName);
773 		return B_ERROR;
774 	}
775 
776 	const size_t hashLength = 8;
777 	uint8 hashTable[hashLength] = { 0 };
778 	hashTable[hashLength - 1] |= 0x80; // broadcast address
779 
780 	status_t result = _WriteRegister(RegMAR, hashLength, hashTable);
781 	if (result != B_OK) {
782 		TRACE_ALWAYS("Error initializing MAR: %#010x.\n", result);
783 		return result;
784 	}
785 
786 	if (join)
787 		fMulticastHashes.PushBack(hash);
788 	else
789 		fMulticastHashes.Remove(hash);
790 
791 	for (int32 i = 0; i < fMulticastHashes.Count(); i++) {
792 		uint32 hash = fMulticastHashes[i] >> 26;
793 		hashTable[hash / 8] |= 1 << (hash % 8);
794 	}
795 
796 	// clear/set pass all multicast bit as required
797 	uint8 control = 0;
798 	result = _ReadRegister(RegRCR, 1, &control);
799 	if (result != B_OK) {
800 		TRACE_ALWAYS("Error reading RCR: %#010x.\n", result);
801 		return result;
802 	}
803 
804 	if (fMulticastHashes.Count() > 0)
805 		control &= ~RCRAllMulticast;
806 	else
807 		control |= RCRAllMulticast;
808 
809 	result = _Write1Register(RegRCR, control);
810 	if (result != B_OK) {
811 		TRACE_ALWAYS("Error writing %#02X to RCR: %#010x.\n", control, result);
812 		return result;
813 	}
814 
815 	result = _WriteRegister(RegMAR, hashLength, hashTable);
816 	if (result != B_OK)
817 		TRACE_ALWAYS("Error writing hash table in MAR: %#010x.\n", result);
818 
819 	return result;
820 }
821 
822 
823 void
824 DavicomDevice::_ReadCallback(void *cookie, int32 status, void *data,
825 	size_t actualLength)
826 {
827 	TRACE_RX("ReadCB: %d bytes; status:%#010x\n", actualLength, status);
828 	DavicomDevice *device = (DavicomDevice *)cookie;
829 	device->fActualLengthRead = actualLength;
830 	device->fStatusRead = status;
831 	device->fStats.readCount++;
832 	release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
833 }
834 
835 
836 void
837 DavicomDevice::_WriteCallback(void *cookie, int32 status, void *data,
838 	size_t actualLength)
839 {
840 	TRACE_TX("WriteCB: %d bytes; status:%#010x\n", actualLength, status);
841 	DavicomDevice *device = (DavicomDevice *)cookie;
842 	device->fActualLengthWrite = actualLength;
843 	device->fStatusWrite = status;
844 	device->fStats.writeCount++;
845 	release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
846 }
847 
848 
849 void
850 DavicomDevice::_NotifyCallback(void *cookie, int32 status, void *data,
851 	size_t actualLength)
852 {
853 	DavicomDevice *device = (DavicomDevice *)cookie;
854 	atomic_add(&device->fInsideNotify, 1);
855 	if (status == B_CANCELED || device->fRemoved) {
856 		atomic_add(&device->fInsideNotify, -1);
857 		return;
858 	}
859 
860 	if (status == B_OK)
861 		device->_OnNotify(actualLength);
862 	else
863 		TRACE_ALWAYS("Status error:%#010x; length:%d\n", status, actualLength);
864 
865 	// schedule next notification buffer
866 	gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyData,
867 		sizeof(DM9601NotifyData), _NotifyCallback, device);
868 	atomic_add(&device->fInsideNotify, -1);
869 }
870 
871 
872 status_t
873 DavicomDevice::_OnNotify(uint32 actualLength)
874 {
875 	if (actualLength != sizeof(DM9601NotifyData)) {
876 		TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n",
877 			actualLength, sizeof(DM9601NotifyData));
878 		return B_BAD_DATA;
879 	}
880 
881 	bool linkIsUp = fNotifyData->LINKST != 0;
882 	fTXBufferFull = fNotifyData->TXFULL != 0;
883 	bool rxOverflow = fNotifyData->RXOV != 0;
884 
885 	bool linkStateChange = (linkIsUp != fHasConnection);
886 	fHasConnection = linkIsUp;
887 
888 	if (linkStateChange) {
889 		if (fHasConnection) {
890 			TRACE("Link is now up at %s Mb/s\n",
891 				fNotifyData->SPEED ? "10" : "100");
892 		} else
893 			TRACE("Link is now down");
894 	}
895 
896 #ifdef UDAV_TRACE
897 	if (gTraceStats) {
898 		if (fNotifyData->TXFULL)
899 			fStats.txFull++;
900 		if (fNotifyData->RXOV)
901 			fStats.rxOverflow++;
902 
903 		if (fNotifyData->ROC)
904 			fStats.rxOvCount += fNotifyData->ROC;
905 
906 		if (fNotifyData->RT)
907 			fStats.runtFrames++;
908 		if (fNotifyData->LCS)
909 			fStats.lateRXCollisions++;
910 		if (fNotifyData->RWTO)
911 			fStats.rwTOs++;
912 		if (fNotifyData->PLE)
913 			fStats.physLayerErros++;
914 		if (fNotifyData->AE)
915 			fStats.alignmentErros++;
916 		if (fNotifyData->CE)
917 			fStats.crcErrors++;
918 		if (fNotifyData->FOE)
919 			fStats.overErrors++;
920 
921 		if (fNotifyData->TSR1.LC)
922 			fStats.lateTXCollisions++;
923 		if (fNotifyData->TSR1.LCR)
924 			fStats.lostOfCarrier++;
925 		if (fNotifyData->TSR1.NC)
926 			fStats.noCarrier++;
927 		if (fNotifyData->TSR1.COL)
928 			fStats.txCollisions++;
929 		if (fNotifyData->TSR1.EC)
930 			fStats.excCollisions++;
931 
932 		if (fNotifyData->TSR2.LC)
933 			fStats.lateTXCollisions++;
934 		if (fNotifyData->TSR2.LCR)
935 			fStats.lostOfCarrier++;
936 		if (fNotifyData->TSR2.NC)
937 			fStats.noCarrier++;
938 		if (fNotifyData->TSR2.COL)
939 			fStats.txCollisions++;
940 		if (fNotifyData->TSR2.EC)
941 			fStats.excCollisions++;
942 
943 		fStats.notifyCount++;
944 	}
945 #endif
946 
947 	if (rxOverflow)
948 		TRACE("RX buffer overflow. %d packets dropped\n", fNotifyData->ROC);
949 
950 	uint8 tsr = 0xfc & *(uint8*)&fNotifyData->TSR1;
951 	if (tsr != 0)
952 		TRACE("TX packet 1: Status %#04x is not OK.\n", tsr);
953 
954 	tsr = 0xfc & *(uint8*)&fNotifyData->TSR2;
955 	if (tsr != 0)
956 		TRACE("TX packet 2: Status %#04x is not OK.\n", tsr);
957 
958 	if (linkStateChange && fLinkStateChangeSem >= B_OK)
959 		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
960 	return B_OK;
961 }
962 
963 
964 status_t
965 DavicomDevice::_GetLinkState(ether_link_state *linkState)
966 {
967 	uint8 registerValue = 0;
968 	status_t result = _ReadRegister(RegNSR, 1, &registerValue);
969 	if (result != B_OK) {
970 		TRACE_ALWAYS("Error reading NSR register! %x\n", result);
971 		return result;
972 	}
973 
974 	if (registerValue & NSRSpeed10)
975 		linkState->speed = 10000000;
976 	else
977 		linkState->speed = 100000000;
978 
979 	linkState->quality = 1000;
980 
981 	linkState->media = IFM_ETHER | IFM_100_TX;
982 	if (fHasConnection) {
983 		linkState->media |= IFM_ACTIVE;
984 		result = _ReadRegister(RegNCR, 1, &registerValue);
985 		if (result != B_OK) {
986 			TRACE_ALWAYS("Error reading NCR register! %x\n", result);
987 			return result;
988 		}
989 
990 		if (registerValue & NCRFullDX)
991 			linkState->media |= IFM_FULL_DUPLEX;
992 		else
993 			linkState->media |= IFM_HALF_DUPLEX;
994 
995 		if (registerValue & NCRLoopback)
996 			linkState->media |= IFM_LOOP;
997 	}
998 
999 	TRACE_STATE("Medium state: %s, %lld MBit/s, %s duplex.\n",
1000 						(linkState->media & IFM_ACTIVE) ? "active" : "inactive",
1001 						linkState->speed / 1000000,
1002 						(linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
1003 
1004 	TRACE_STATS("tx:%d rx:%d rxCn:%d rtF:%d lRxC:%d rwTO:%d PLE:%d AE:%d CE:%d "
1005 				"oE:%d ltxC:%d lCR:%d nC:%d txC:%d exC:%d r:%d w:%d n:%d\n",
1006 					fStats.txFull, fStats.rxOverflow, fStats.rxOvCount,
1007 					fStats.runtFrames, fStats.lateRXCollisions, fStats.rwTOs,
1008 					fStats.physLayerErros, fStats.alignmentErros,
1009 					fStats.crcErrors, fStats.overErrors,
1010 					fStats.lateTXCollisions, fStats.lostOfCarrier,
1011 					fStats.noCarrier, fStats.txCollisions, fStats.excCollisions,
1012 					fStats.readCount, fStats.writeCount, fStats.notifyCount);
1013 	return B_OK;
1014 }
1015 
1016 
1017 status_t
1018 DavicomDevice::_ReadRegister(uint8 reg, size_t size, uint8* buffer)
1019 {
1020 	if (size > 255)
1021 		return B_BAD_VALUE;
1022 
1023 	size_t actualLength = 0;
1024 	status_t result = gUSBModule->send_request(fDevice,
1025 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
1026 		ReqReadRegister, 0, reg, size, buffer, &actualLength);
1027 
1028 	if (size != actualLength) {
1029 		TRACE_ALWAYS("Size mismatch reading register ! asked %d got %d",
1030 			size, actualLength);
1031 	}
1032 
1033 	return result;
1034 }
1035 
1036 
1037 status_t
1038 DavicomDevice::_WriteRegister(uint8 reg, size_t size, uint8* buffer)
1039 {
1040 	if (size > 255)
1041 		return B_BAD_VALUE;
1042 
1043 	size_t actualLength = 0;
1044 
1045 	status_t result = gUSBModule->send_request(fDevice,
1046 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
1047 		ReqWriteRegister, 0, reg, size, buffer, &actualLength);
1048 
1049 	return result;
1050 }
1051 
1052 
1053 status_t
1054 DavicomDevice::_Write1Register(uint8 reg, uint8 value)
1055 {
1056 	size_t actualLength = 0;
1057 
1058 	status_t result = gUSBModule->send_request(fDevice,
1059 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
1060 		ReqWriteRegisterByte, value, reg, 0, NULL, &actualLength);
1061 
1062 	return result;
1063 }
1064 
1065 
1066 status_t
1067 DavicomDevice::_ReadMII(uint8 reg, uint16* data)
1068 {
1069 	// select PHY and set PHY register address
1070 	status_t result = _Write1Register(RegEPAR, EPARIntPHY | (reg & EPARMask));
1071 	if (result != B_OK) {
1072 		TRACE_ALWAYS("Failed to set MII address %#x. Error:%#x\n", reg, result);
1073 		return result;
1074 	}
1075 
1076 	// select PHY operation and initiate reading
1077 	result = _Write1Register(RegEPCR, EPCROpSelect | EPCRRegRead);
1078 	if (result != B_OK) {
1079 		TRACE_ALWAYS("Failed to starting MII reading. Error:%#x\n", result);
1080 		return result;
1081 	}
1082 
1083 	// finalize writing
1084 	uint8 control = 0;
1085 	result = _ReadRegister(RegEPCR, 1, &control);
1086 	if (result != B_OK) {
1087 		TRACE_ALWAYS("Failed to read EPCR register. Error:%#x\n", result);
1088 		return result;
1089 	}
1090 
1091 	result = _Write1Register(RegEPCR, control & ~EPCRRegRead);
1092 	if (result != B_OK) {
1093 		TRACE_ALWAYS("Failed to write EPCR register. Error:%#x\n", result);
1094 		return result;
1095 	}
1096 
1097 	// retrieve the result from data registers
1098 	uint8 values[2] = { 0 };
1099 	result = _ReadRegister(RegEPDRL, 2, values);
1100 	if (result != B_OK) {
1101 		TRACE_ALWAYS("Failed to retrieve data %#x. Error:%#x\n", data, result);
1102 		return result;
1103 	}
1104 
1105 	*data = values[0] | values[1] << 8;
1106 	return result;
1107 }
1108 
1109 
1110 status_t
1111 DavicomDevice::_WriteMII(uint8 reg, uint16 data)
1112 {
1113 	// select PHY and set PHY register address
1114 	status_t result = _Write1Register(RegEPAR, EPARIntPHY | (reg & EPARMask));
1115 	if (result != B_OK) {
1116 		TRACE_ALWAYS("Failed to set MII address %#x. Error:%#x\n", reg, result);
1117 		return result;
1118 	}
1119 
1120 	// put the value to data register
1121 	uint8 values[] = { (uint8)(data & 0xff), (uint8)((data >> 8) & 0xff) };
1122 	result = _WriteRegister(RegEPDRL, sizeof(uint16), values);
1123 	if (result != B_OK) {
1124 		TRACE_ALWAYS("Failed to put data %#x. Error:%#x\n", data, result);
1125 		return result;
1126 	}
1127 
1128 	// select PHY operation and initiate writing
1129 	result = _Write1Register(RegEPCR, EPCROpSelect | EPCRRegWrite);
1130 	if (result != B_OK) {
1131 		TRACE_ALWAYS("Failed to starting MII wrintig. Error:%#x\n", result);
1132 		return result;
1133 	}
1134 
1135 	// finalize writing
1136 	uint8 control = 0;
1137 	result = _ReadRegister(RegEPCR, 1, &control);
1138 	if (result != B_OK) {
1139 		TRACE_ALWAYS("Failed to read EPCR register. Error:%#x\n", result);
1140 		return result;
1141 	}
1142 
1143 	result = _Write1Register(RegEPCR, control & ~EPCRRegWrite);
1144 	if (result != B_OK)
1145 		TRACE_ALWAYS("Failed to write EPCR register. Error:%#x\n", result);
1146 
1147 	return result;
1148 }
1149 
1150 
1151 status_t
1152 DavicomDevice::_InitMII()
1153 {
1154 	uint16 control = 0;
1155 	status_t result = _ReadMII(RegBMCR, &control);
1156 	if (result != B_OK) {
1157 		TRACE_ALWAYS("Failed to read MII BMCR register. Error:%#x\n", result);
1158 		return result;
1159 	}
1160 
1161 	result = _WriteMII(RegBMCR, control & ~BMCRIsolate);
1162 	if (result != B_OK) {
1163 		TRACE_ALWAYS("Failed to clear isolate PHY. Error:%#x\n", result);
1164 		return result;
1165 	}
1166 
1167 	result = _WriteMII(0, BMCRReset);
1168 	if (result != B_OK) {
1169 		TRACE_ALWAYS("Failed to reset BMCR register. Error:%#x\n", result);
1170 		return result;
1171 	}
1172 
1173 	uint16 id01 = 0, id02 = 0;
1174 	result = _ReadMII(RegPHYID1, &id01);
1175 	if (result != B_OK) {
1176 		TRACE_ALWAYS("Failed to read PHY ID 0. Error:%#x\n", result);
1177 		return result;
1178 	}
1179 
1180 	result = _ReadMII(RegPHYID2, &id02);
1181 	if (result != B_OK) {
1182 		TRACE_ALWAYS("Failed to read PHY ID 1. Error:%#x\n", result);
1183 		return result;
1184 	}
1185 
1186 	TRACE_ALWAYS("MII Info: OUI:%04x; Model:%04x; rev:%02x.\n",
1187 			MII_OUI(id01, id02), MII_MODEL(id02), MII_REV(id02));
1188 
1189 	return result;
1190 }
1191 
1192 
1193 status_t
1194 DavicomDevice::_EnableInterrupts(bool enable)
1195 {
1196 	uint8 control = 0;
1197 	status_t result = _ReadRegister(RegUSBC, 1, &control);
1198 	if (result != B_OK) {
1199 		TRACE_ALWAYS("Error of reading USB control register:%#010x\n", result);
1200 		return result;
1201 	}
1202 
1203 	if (enable) {
1204 		control |= USBCIntAck;
1205 		control &= ~USBCIntNAck;
1206 	} else {
1207 		control &= ~USBCIntAck;
1208 	}
1209 
1210 	result = _Write1Register(RegUSBC, control);
1211 	if (result != B_OK)
1212 		TRACE_ALWAYS("Error of setting USB control register:%#010x\n", result);
1213 
1214 	return result;
1215 }
1216 
1217