xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_asix/ASIXDevice.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 "ASIXDevice.h"
15 
16 #include <stdio.h>
17 
18 #include "ASIXVendorRequests.h"
19 #include "Driver.h"
20 #include "Settings.h"
21 
22 
23 // frame header used during transfer data
24 struct TRXHeader {
25 	uint16	fLength;
26 	uint16	fInvertedLength;
27 
28 	TRXHeader(uint16 length = 0) { SetLength(length); }
29 	bool 	IsValid() { return (fLength ^ fInvertedLength) == 0xffff; }
30 	uint16	Length()  { return fLength; }
31 	// TODO: low-endian convertion?
32 	void	SetLength(uint16 length) {
33 				fLength = length;
34 				fInvertedLength = ~fLength;
35 			}
36 };
37 
38 
39 ASIXDevice::ASIXDevice(usb_device device, DeviceInfo& deviceInfo)
40 	:
41 	fDevice(device),
42 	fStatus(B_ERROR),
43 	fOpen(false),
44 	fRemoved(false),
45 	fHasConnection(false),
46 	fNonBlocking(false),
47 	fInsideNotify(0),
48 	fFrameSize(0),
49 	fNotifyEndpoint(0),
50 	fReadEndpoint(0),
51 	fWriteEndpoint(0),
52 	fActualLengthRead(0),
53 	fActualLengthWrite(0),
54 	fStatusRead(B_OK),
55 	fStatusWrite(B_OK),
56 	fNotifyReadSem(-1),
57 	fNotifyWriteSem(-1),
58 	fNotifyBuffer(NULL),
59 	fNotifyBufferLength(0),
60 	fLinkStateChangeSem(-1),
61 	fUseTRXHeader(false),
62 	fReadNodeIDRequest(kInvalidRequest)
63 {
64 	fDeviceInfo = deviceInfo;
65 
66 	fIPG[0] = 0x15;
67 	fIPG[1] = 0x0c;
68 	fIPG[2] = 0x12;
69 
70 	memset(&fMACAddress, 0, sizeof(fMACAddress));
71 
72 	fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
73 	if (fNotifyReadSem < B_OK) {
74 		TRACE_ALWAYS("Error of creating read notify semaphore:%#010x\n",
75 			fNotifyReadSem);
76 		return;
77 	}
78 
79 	fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
80 	if (fNotifyWriteSem < B_OK) {
81 		TRACE_ALWAYS("Error of creating write notify semaphore:%#010x\n",
82 			fNotifyWriteSem);
83 		return;
84 	}
85 
86 	if (_SetupEndpoints() != B_OK) {
87 		return;
88 	}
89 
90 	// must be set in derived class constructor
91 	// fStatus = B_OK;
92 }
93 
94 
95 ASIXDevice::~ASIXDevice()
96 {
97 	if (fNotifyReadSem >= B_OK)
98 		delete_sem(fNotifyReadSem);
99 	if (fNotifyWriteSem >= B_OK)
100 		delete_sem(fNotifyWriteSem);
101 
102 	if (!fRemoved) // ???
103 		gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
104 
105 	if (fNotifyBuffer)
106 		free(fNotifyBuffer);
107 }
108 
109 
110 status_t
111 ASIXDevice::Open(uint32 flags)
112 {
113 	if (fOpen)
114 		return B_BUSY;
115 	if (fRemoved)
116 		return B_ERROR;
117 
118 	status_t result = StartDevice();
119 	if (result != B_OK) {
120 		return result;
121 	}
122 
123 	// setup state notifications
124 	result = gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer,
125 		fNotifyBufferLength, _NotifyCallback, this);
126 	if (result != B_OK) {
127 		TRACE_ALWAYS("Error of requesting notify interrupt:%#010x\n", result);
128 		return result;
129 	}
130 
131 	fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK;
132 	fOpen = true;
133 	return result;
134 }
135 
136 
137 status_t
138 ASIXDevice::Close()
139 {
140 	if (fRemoved) {
141 		fOpen = false;
142 		return B_OK;
143 	}
144 
145 	// wait until possible notification handling finished...
146 	while (atomic_add(&fInsideNotify, 0) != 0)
147 		snooze(100);
148 	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
149 	gUSBModule->cancel_queued_transfers(fReadEndpoint);
150 	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
151 
152 	fOpen = false;
153 
154 	return StopDevice();
155 }
156 
157 
158 status_t
159 ASIXDevice::Free()
160 {
161 	return B_OK;
162 }
163 
164 
165 status_t
166 ASIXDevice::Read(uint8 *buffer, size_t *numBytes)
167 {
168 	size_t numBytesToRead = *numBytes;
169 	*numBytes = 0;
170 
171 	if (fRemoved) {
172 		TRACE_ALWAYS("Error of receiving %d bytes from removed device.\n",
173 			numBytesToRead);
174 		return B_DEVICE_NOT_FOUND;
175 	}
176 
177 	TRACE_FLOW("Request %d bytes.\n", numBytesToRead);
178 
179 	TRXHeader header;
180 	iovec rxData[] = {
181 		{ &header, sizeof(TRXHeader) },
182 		{ buffer,  numBytesToRead }
183 	};
184 
185 	size_t startIndex = fUseTRXHeader ? 0 : 1 ;
186 	size_t chunkCount = fUseTRXHeader ? 2 : 1 ;
187 
188 	status_t result = gUSBModule->queue_bulk_v(fReadEndpoint,
189 		&rxData[startIndex], chunkCount, _ReadCallback, this);
190 
191 	if (result != B_OK) {
192 		TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result);
193 		return result;
194 	}
195 
196 	uint32 flags = B_CAN_INTERRUPT | (fNonBlocking ? B_TIMEOUT : 0);
197 	result = acquire_sem_etc(fNotifyReadSem, 1, flags, 0);
198 	if (result < B_OK) {
199 		TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result);
200 		return result;
201 	}
202 
203 	if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) {
204 		TRACE_ALWAYS("Device status error:%#010x\n", fStatusRead);
205 		result = gUSBModule->clear_feature(fReadEndpoint,
206 			USB_FEATURE_ENDPOINT_HALT);
207 		if (result != B_OK) {
208 			TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n",
209 				result);
210 			return result;
211 		}
212 	}
213 
214 	if (fUseTRXHeader) {
215 		if (fActualLengthRead < sizeof(TRXHeader)) {
216 			TRACE_ALWAYS("Error: no place for TRXHeader:only %d of %d bytes.\n",
217 				fActualLengthRead, sizeof(TRXHeader));
218 			return B_ERROR; // TODO: ???
219 		}
220 
221 		if (!header.IsValid()) {
222 			TRACE_ALWAYS("Error:TRX Header is invalid: len:%#04x; ilen:%#04x\n",
223 				header.fLength, header.fInvertedLength);
224 			return B_ERROR; // TODO: ???
225 		}
226 
227 		*numBytes = header.Length();
228 
229 		// the device pushes packets 16bit aligned
230 		if (fActualLengthRead - sizeof(TRXHeader) > header.Length()
231 				+ (header.Length() % 2u)) {
232 			TRACE_ALWAYS("MISMATCH of the frame length: hdr %d; received:%d\n",
233 				header.Length(), fActualLengthRead - sizeof(TRXHeader));
234 		} else if (fActualLengthRead - sizeof(TRXHeader) < header.Length()) {
235 			TRACE_ALWAYS("Error: received too little data: hdr %d; received:%d\n",
236 				header.Length(), fActualLengthRead - sizeof(TRXHeader));
237 		}
238 
239 	} else {
240 
241 		*numBytes = fActualLengthRead;
242 	}
243 
244 	TRACE_FLOW("Read %d bytes.\n", *numBytes);
245 	return B_OK;
246 }
247 
248 
249 status_t
250 ASIXDevice::Write(const uint8 *buffer, size_t *numBytes)
251 {
252 	size_t numBytesToWrite = *numBytes;
253 	*numBytes = 0;
254 
255 	if (fRemoved) {
256 		TRACE_ALWAYS("Error of writing %d bytes to removed device.\n",
257 			numBytesToWrite);
258 		return B_DEVICE_NOT_FOUND;
259 	}
260 
261 	TRACE_FLOW("Write %d bytes.\n", numBytesToWrite);
262 
263 	TRXHeader header(numBytesToWrite);
264 	iovec txData[] = {
265 		{ &header, sizeof(TRXHeader) },
266 		{ (uint8*)buffer, numBytesToWrite }
267 	};
268 
269 	size_t startIndex = fUseTRXHeader ? 0 : 1 ;
270 	size_t chunkCount = fUseTRXHeader ? 2 : 1 ;
271 
272 	status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint,
273 		&txData[startIndex], chunkCount, _WriteCallback, this);
274 
275 	if (result != B_OK) {
276 		TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result);
277 		return result;
278 	}
279 
280 	result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0);
281 
282 	if (result < B_OK) {
283 		TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result);
284 		return result;
285 	}
286 
287 	if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) {
288 		TRACE_ALWAYS("Device status error:%#010x\n", fStatusWrite);
289 		result = gUSBModule->clear_feature(fWriteEndpoint,
290 			USB_FEATURE_ENDPOINT_HALT);
291 		if (result != B_OK) {
292 			TRACE_ALWAYS("Error during clearing of HALT state:%#010x\n", result);
293 			return result;
294 		}
295 	}
296 
297 	if (fUseTRXHeader) {
298 		*numBytes = fActualLengthWrite - sizeof(TRXHeader);
299 	} else {
300 		*numBytes = fActualLengthWrite;
301 	}
302 
303 	TRACE_FLOW("Written %d bytes.\n", *numBytes);
304 	return B_OK;
305 }
306 
307 
308 status_t
309 ASIXDevice::Control(uint32 op, void *buffer, size_t length)
310 {
311 	switch (op) {
312 		case ETHER_INIT:
313 			return B_OK;
314 
315 		case ETHER_GETADDR:
316 			memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
317 			return B_OK;
318 
319 		case ETHER_GETFRAMESIZE:
320 			*(uint32 *)buffer = fFrameSize;
321 			return B_OK;
322 
323 		case ETHER_NONBLOCK:
324 			TRACE("ETHER_NONBLOCK\n");
325 			fNonBlocking = *((uint8*)buffer);
326 			return B_OK;
327 
328 		case ETHER_SETPROMISC:
329 			TRACE("ETHER_SETPROMISC\n");
330 			return SetPromiscuousMode(*((uint8*)buffer));
331 
332 		case ETHER_ADDMULTI:
333 			TRACE("ETHER_ADDMULTI\n");
334 			return ModifyMulticastTable(true, (ether_address_t*)buffer);
335 
336 		case ETHER_REMMULTI:
337 			TRACE("ETHER_REMMULTI\n");
338 			return ModifyMulticastTable(false, (ether_address_t*)buffer);
339 
340 		case ETHER_SET_LINK_STATE_SEM:
341 			fLinkStateChangeSem = *(sem_id *)buffer;
342 			return B_OK;
343 
344 		case ETHER_GET_LINK_STATE:
345 			return GetLinkState((ether_link_state *)buffer);
346 
347 		default:
348 			TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op);
349 	}
350 
351 	return B_DEV_INVALID_IOCTL;
352 }
353 
354 
355 void
356 ASIXDevice::Removed()
357 {
358 	fRemoved = true;
359 	fHasConnection = false;
360 
361 	// the notify hook is different from the read and write hooks as it does
362 	// itself schedule traffic (while the other hooks only release a semaphore
363 	// to notify another thread which in turn safly checks for the removed
364 	// case) - so we must ensure that we are not inside the notify hook anymore
365 	// before returning, as we would otherwise violate the promise not to use
366 	// any of the pipes after returning from the removed hook
367 	while (atomic_add(&fInsideNotify, 0) != 0)
368 		snooze(100);
369 
370 	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
371 	gUSBModule->cancel_queued_transfers(fReadEndpoint);
372 	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
373 
374 	if (fLinkStateChangeSem >= B_OK)
375 		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
376 }
377 
378 
379 status_t
380 ASIXDevice::SetupDevice(bool deviceReplugged)
381 {
382 	ether_address address;
383 	status_t result = ReadMACAddress(&address);
384 	if (result != B_OK) {
385 		TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result);
386 		return result;
387 	}
388 
389 	TRACE("MAC address is:%02x:%02x:%02x:%02x:%02x:%02x\n",
390 		address.ebyte[0], address.ebyte[1], address.ebyte[2],
391 		address.ebyte[3], address.ebyte[4], address.ebyte[5]);
392 
393 	if (deviceReplugged) {
394 		// this might be the same device that was replugged - read the MAC
395 		// address (which should be at the same index) to make sure
396 		if (memcmp(&address, &fMACAddress, sizeof(address)) != 0) {
397 			TRACE_ALWAYS("Cannot replace device with MAC address:"
398 				"%02x:%02x:%02x:%02x:%02x:%02x\n", fMACAddress.ebyte[0],
399 				fMACAddress.ebyte[1], fMACAddress.ebyte[2],
400 				fMACAddress.ebyte[3], fMACAddress.ebyte[4],
401 				fMACAddress.ebyte[5]);
402 			return B_BAD_VALUE; // is not the same
403 		}
404 	} else
405 		fMACAddress = address;
406 
407 	return B_OK;
408 }
409 
410 
411 status_t
412 ASIXDevice::CompareAndReattach(usb_device device)
413 {
414 	const usb_device_descriptor *deviceDescriptor
415 		= gUSBModule->get_device_descriptor(device);
416 
417 	if (deviceDescriptor == NULL) {
418 		TRACE_ALWAYS("Error of getting USB device descriptor.\n");
419 		return B_ERROR;
420 	}
421 
422 	if (deviceDescriptor->vendor_id != fDeviceInfo.VendorId()
423 		&& deviceDescriptor->product_id != fDeviceInfo.ProductId()) {
424 		// this certainly isn't the same device
425 		return B_BAD_VALUE;
426 	}
427 
428 	// this is the same device that was replugged - clear the removed state,
429 	// re-setup the endpoints and transfers and open the device if it was
430 	// previously opened
431 	fDevice = device;
432 	fRemoved = false;
433 	status_t result = _SetupEndpoints();
434 	if (result != B_OK) {
435 		fRemoved = true;
436 		return result;
437 	}
438 
439 	// we need to setup hardware on device replug
440 	result = SetupDevice(true);
441 	if (result != B_OK) {
442 		return result;
443 	}
444 
445 	if (fOpen) {
446 		fOpen = false;
447 		result = Open(fNonBlocking ? O_NONBLOCK : 0);
448 	}
449 
450 	return result;
451 }
452 
453 
454 status_t
455 ASIXDevice::_SetupEndpoints()
456 {
457 	const usb_configuration_info *config
458 		= gUSBModule->get_nth_configuration(fDevice, 0);
459 
460 	if (config == NULL) {
461 		TRACE_ALWAYS("Error of getting USB device configuration.\n");
462 		return B_ERROR;
463 	}
464 
465 	if (config->interface_count <= 0) {
466 		TRACE_ALWAYS("Error:no interfaces found in USB device configuration\n");
467 		return B_ERROR;
468 	}
469 
470 	usb_interface_info *interface = config->interface[0].active;
471 	if (interface == 0) {
472 		TRACE_ALWAYS("Error:invalid active interface in "
473 												"USB device configuration\n");
474 		return B_ERROR;
475 	}
476 
477 	int notifyEndpoint = -1;
478 	int readEndpoint   = -1;
479 	int writeEndpoint  = -1;
480 
481 	for (size_t ep = 0; ep < interface->endpoint_count; ep++) {
482 		usb_endpoint_descriptor *epd = interface->endpoint[ep].descr;
483 		if ((epd->attributes & USB_ENDPOINT_ATTR_MASK)
484 			== USB_ENDPOINT_ATTR_INTERRUPT) {
485 			notifyEndpoint = ep;
486 			continue;
487 		}
488 
489 		if ((epd->attributes & USB_ENDPOINT_ATTR_MASK)
490 			!= USB_ENDPOINT_ATTR_BULK) {
491 			TRACE_ALWAYS("Error: USB endpoint type %#04x is unknown.\n",
492 				epd->attributes);
493 			continue;
494 		}
495 
496 		if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK)
497 			== USB_ENDPOINT_ADDR_DIR_IN) {
498 			readEndpoint = ep;
499 			continue;
500 		}
501 
502 		if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK)
503 			== USB_ENDPOINT_ADDR_DIR_OUT) {
504 			writeEndpoint = ep;
505 			continue;
506 		}
507 	}
508 
509 	if (notifyEndpoint == -1 || readEndpoint == -1 || writeEndpoint == -1) {
510 		TRACE_ALWAYS("Error: not all USB endpoints were found: "
511 			"notify:%d; read:%d; write:%d\n", notifyEndpoint, readEndpoint,
512 			writeEndpoint);
513 		return B_ERROR;
514 	}
515 
516 	gUSBModule->set_configuration(fDevice, config);
517 
518 	fNotifyEndpoint = interface->endpoint[notifyEndpoint].handle;
519 	fReadEndpoint   = interface->endpoint[readEndpoint].handle;
520 	fWriteEndpoint  = interface->endpoint[writeEndpoint].handle;
521 
522 	return B_OK;
523 }
524 
525 
526 status_t
527 ASIXDevice::ReadMACAddress(ether_address_t *address)
528 {
529 	size_t actual_length = 0;
530 	status_t result = gUSBModule->send_request(fDevice,
531 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, fReadNodeIDRequest,
532 		0, 0, sizeof(ether_address), address, &actual_length);
533 
534 	if (result != B_OK) {
535 		TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result);
536 		return result;
537 	}
538 
539 	if (actual_length != sizeof(ether_address)) {
540 		TRACE_ALWAYS("Mismatch of NODE ID data size: %d instead of %d bytes\n",
541 										actual_length, sizeof(ether_address));
542 		return B_ERROR;
543 	}
544 
545 	return B_OK;
546 }
547 
548 
549 status_t
550 ASIXDevice::ReadRXControlRegister(uint16 *rxcontrol)
551 {
552 	size_t actual_length = 0;
553 	*rxcontrol = 0;
554 
555 	status_t result = gUSBModule->send_request(fDevice,
556 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_RX_CONTROL,
557 		0, 0, sizeof(*rxcontrol), rxcontrol, &actual_length);
558 
559 	if (sizeof(*rxcontrol) != actual_length) {
560 		TRACE_ALWAYS("Mismatch during reading RX control register."
561 			"Read %d bytes instead of %d.\n", actual_length,
562 			sizeof(*rxcontrol));
563 	}
564 
565 	return result;
566 }
567 
568 
569 status_t
570 ASIXDevice::WriteRXControlRegister(uint16 rxcontrol)
571 {
572 	status_t result = gUSBModule->send_request(fDevice,
573 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_RX_CONTROL,
574 		rxcontrol, 0, 0, 0, 0);
575 	return result;
576 }
577 
578 
579 status_t
580 ASIXDevice::StopDevice()
581 {
582 	status_t result = WriteRXControlRegister(0);
583 
584 	if (result != B_OK)
585 		TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result);
586 
587 	TRACE_RET(result);
588 	return result;
589 }
590 
591 
592 status_t
593 ASIXDevice::SetPromiscuousMode(bool on)
594 {
595 	uint16 rxcontrol = 0;
596 
597 	status_t result = ReadRXControlRegister(&rxcontrol);
598 	if (result != B_OK) {
599 		TRACE_ALWAYS("Error of reading RX Control:%#010x\n", result);
600 		return result;
601 	}
602 
603 	if (on)
604 		rxcontrol |= RXCTL_PROMISCUOUS;
605 	else
606 		rxcontrol &= ~RXCTL_PROMISCUOUS;
607 
608 	result = WriteRXControlRegister(rxcontrol);
609 
610 	if (result != B_OK ) {
611 		TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n",
612 			rxcontrol, result);
613 	}
614 
615 	TRACE_RET(result);
616 	return result;
617 }
618 
619 
620 uint32
621 ASIXDevice::EthernetCRC32(const uint8* buffer, size_t length)
622 {
623 	uint32 result = 0xffffffff;
624 	for (size_t i = 0; i < length; i++) {
625 		uint8 data = *buffer++;
626 		for (int bit = 0; bit < 8; bit++, data >>= 1) {
627 			uint32 carry = ((result & 0x80000000) ? 1 : 0) ^ (data & 0x01);
628 			result <<= 1;
629 			if (carry != 0)
630 				result = (result ^ 0x04c11db6) | carry;
631 		}
632 	}
633 	return result;
634 }
635 
636 
637 status_t
638 ASIXDevice::ModifyMulticastTable(bool join, ether_address_t* group)
639 {
640 	char groupName[6 * 3 + 1] = { 0 };
641 	sprintf(groupName, "%02x:%02x:%02x:%02x:%02x:%02x",
642 		group->ebyte[0], group->ebyte[1], group->ebyte[2],
643 		group->ebyte[3], group->ebyte[4], group->ebyte[5]);
644 	TRACE("%s multicast group %s\n", join ? "Joining" : "Leaving", groupName);
645 
646 	uint32 hash = EthernetCRC32(group->ebyte, 6);
647 	bool isInTable = fMulticastHashes.Find(hash) != fMulticastHashes.End();
648 
649 	if (isInTable && join)
650 		return B_OK; // already listed - nothing to do
651 
652 	if (!isInTable && !join) {
653 		TRACE_ALWAYS("Cannot leave unlisted multicast group %s!\n", groupName);
654 		return B_ERROR;
655 	}
656 
657 	const size_t hashLength = 8;
658 	uint8 hashTable[hashLength] = { 0 };
659 
660 	if (join)
661 		fMulticastHashes.PushBack(hash);
662 	else
663 		fMulticastHashes.Remove(hash);
664 
665 	for (int32 i = 0; i < fMulticastHashes.Count(); i++) {
666 		uint32 hash = fMulticastHashes[i] >> 26;
667 		hashTable[hash / 8] |= 1 << (hash % 8);
668 	}
669 
670 	uint16 rxcontrol = 0;
671 
672 	status_t result = ReadRXControlRegister(&rxcontrol);
673 	if (result != B_OK) {
674 		TRACE_ALWAYS("Error of reading RX Control:%#010x\n", result);
675 		return result;
676 	}
677 
678 	if (fMulticastHashes.Count() > 0)
679 		rxcontrol |= RXCTL_MULTICAST;
680 	else
681 		rxcontrol &= ~RXCTL_MULTICAST;
682 
683 	// write multicast hash table
684 	size_t actualLength = 0;
685 	result = gUSBModule->send_request(fDevice,
686 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_MF_ARRAY,
687 		0, 0, hashLength, hashTable, &actualLength);
688 
689 	if (result != B_OK) {
690 		TRACE_ALWAYS("Error writing hash table in MAR: %#010x.\n", result);
691 		return result;
692 	}
693 
694 	if (actualLength != hashLength)
695 		TRACE_ALWAYS("Incomplete writing of hash table: %d bytes of %d\n",
696 			actualLength, hashLength);
697 
698 	result = WriteRXControlRegister(rxcontrol);
699 	if (result != B_OK)
700 		TRACE_ALWAYS("Error writing %#02X to RXC:%#010x.\n", rxcontrol, result);
701 
702 	return result;
703 }
704 
705 
706 void
707 ASIXDevice::_ReadCallback(void *cookie, int32 status, void *data,
708 	size_t actualLength)
709 {
710 	TRACE_FLOW("ReadCB: %d bytes; status:%#010x\n", actualLength, status);
711 	ASIXDevice *device = (ASIXDevice *)cookie;
712 	device->fActualLengthRead = actualLength;
713 	device->fStatusRead = status;
714 	release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
715 }
716 
717 
718 void
719 ASIXDevice::_WriteCallback(void *cookie, int32 status, void *data,
720 	size_t actualLength)
721 {
722 	TRACE_FLOW("WriteCB: %d bytes; status:%#010x\n", actualLength, status);
723 	ASIXDevice *device = (ASIXDevice *)cookie;
724 	device->fActualLengthWrite = actualLength;
725 	device->fStatusWrite = status;
726 	release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
727 }
728 
729 
730 void
731 ASIXDevice::_NotifyCallback(void *cookie, int32 status, void *data,
732 	size_t actualLength)
733 {
734 	ASIXDevice *device = (ASIXDevice *)cookie;
735 	atomic_add(&device->fInsideNotify, 1);
736 	if (status == B_CANCELED || device->fRemoved) {
737 		atomic_add(&device->fInsideNotify, -1);
738 		return;
739 	}
740 
741 	if (status != B_OK) {
742 		TRACE_ALWAYS("Device status error:%#010x\n", status);
743 		status_t result = gUSBModule->clear_feature(device->fNotifyEndpoint,
744 													USB_FEATURE_ENDPOINT_HALT);
745 		if (result != B_OK)
746 			TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n",
747 				result);
748 	}
749 
750 	// parse data in overriden class
751 	device->OnNotify(actualLength);
752 
753 	// schedule next notification buffer
754 	gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer,
755 		device->fNotifyBufferLength, _NotifyCallback, device);
756 	atomic_add(&device->fInsideNotify, -1);
757 }
758