xref: /haiku/src/add-ons/kernel/busses/usb/uhci.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
1 /*
2  * Copyright 2004-2006, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Lotz <mmlr@mlotz.ch>
7  *		Niels S. Reedijk
8  */
9 
10 #include <module.h>
11 #include <PCI.h>
12 #include <USB3.h>
13 #include <KernelExport.h>
14 #include <stdlib.h>
15 
16 #include "uhci.h"
17 #include "uhci_hardware.h"
18 #include "usb_p.h"
19 
20 
21 pci_module_info *UHCI::sPCIModule = NULL;
22 
23 
24 static int32
25 uhci_std_ops(int32 op, ...)
26 {
27 	switch (op) {
28 		case B_MODULE_INIT:
29 			TRACE(("usb_uhci_module: init module\n"));
30 			return B_OK;
31 		case B_MODULE_UNINIT:
32 			TRACE(("usb_uhci_module: uninit module\n"));
33 			break;
34 		default:
35 			return EINVAL;
36 	}
37 
38 	return B_OK;
39 }
40 
41 
42 host_controller_info uhci_module = {
43 	{
44 		"busses/usb/uhci/nielx",
45 		0,
46 		uhci_std_ops
47 	},
48 	NULL,
49 	UHCI::AddTo
50 };
51 
52 
53 module_info *modules[] = {
54 	(module_info *)&uhci_module,
55 	NULL
56 };
57 
58 
59 //
60 // #pragma mark -
61 //
62 
63 
64 #ifdef TRACE_USB
65 
66 void
67 print_descriptor_chain(uhci_td *descriptor)
68 {
69 	while (descriptor) {
70 		dprintf("ph: 0x%08lx; lp: 0x%08lx; vf: %s; q: %s; t: %s; st: 0x%08lx; to: 0x%08lx\n",
71 			descriptor->this_phy & 0xffffffff, descriptor->link_phy & 0xfffffff0,
72 			descriptor->link_phy & 0x4 ? "y" : "n",
73 			descriptor->link_phy & 0x2 ? "qh" : "td",
74 			descriptor->link_phy & 0x1 ? "y" : "n",
75 			descriptor->status, descriptor->token);
76 
77 		if (descriptor->link_phy & TD_TERMINATE)
78 			break;
79 
80 		descriptor = (uhci_td *)descriptor->link_log;
81 	}
82 }
83 
84 #endif // TRACE_USB
85 
86 
87 //
88 // #pragma mark -
89 //
90 
91 
92 Queue::Queue(Stack *stack)
93 {
94 	fStack = stack;
95 
96 	if (benaphore_init(&fLock, "uhci queue lock") < B_OK) {
97 		TRACE_ERROR(("usb_uhci: failed to create queue lock\n"));
98 		return;
99 	}
100 
101 	void *physicalAddress;
102 	fStatus = fStack->AllocateChunk((void **)&fQueueHead, &physicalAddress,
103 		sizeof(uhci_qh));
104 	if (fStatus < B_OK)
105 		return;
106 
107 	fQueueHead->this_phy = (addr_t)physicalAddress;
108 	fQueueHead->element_phy = QH_TERMINATE;
109 
110 	fStrayDescriptor = NULL;
111 	fQueueTop = NULL;
112 }
113 
114 
115 Queue::~Queue()
116 {
117 	Lock();
118 	benaphore_destroy(&fLock);
119 
120 	fStack->FreeChunk(fQueueHead, (void *)fQueueHead->this_phy, sizeof(uhci_qh));
121 
122 	if (fStrayDescriptor)
123 		fStack->FreeChunk(fStrayDescriptor, (void *)fStrayDescriptor->this_phy,
124 			sizeof(uhci_td));
125 }
126 
127 
128 status_t
129 Queue::InitCheck()
130 {
131 	return fStatus;
132 }
133 
134 
135 bool
136 Queue::Lock()
137 {
138 	return (benaphore_lock(&fLock) == B_OK);
139 }
140 
141 
142 void
143 Queue::Unlock()
144 {
145 	benaphore_unlock(&fLock);
146 }
147 
148 
149 status_t
150 Queue::LinkTo(Queue *other)
151 {
152 	if (!other)
153 		return B_BAD_VALUE;
154 
155 	if (!Lock())
156 		return B_ERROR;
157 
158 	fQueueHead->link_phy = other->fQueueHead->this_phy | QH_NEXT_IS_QH;
159 	fQueueHead->link_log = other->fQueueHead;
160 	Unlock();
161 
162 	return B_OK;
163 }
164 
165 
166 status_t
167 Queue::TerminateByStrayDescriptor()
168 {
169 	// According to the *BSD USB sources, there needs to be a stray transfer
170 	// descriptor in order to get some chipset to work nicely (like the PIIX).
171 	void *physicalAddress;
172 	status_t result = fStack->AllocateChunk((void **)&fStrayDescriptor,
173 		&physicalAddress, sizeof(uhci_td));
174 	if (result < B_OK) {
175 		TRACE_ERROR(("usb_uhci: failed to allocate a stray transfer descriptor\n"));
176 		return result;
177 	}
178 
179 	fStrayDescriptor->status = 0;
180 	fStrayDescriptor->this_phy = (addr_t)physicalAddress;
181 	fStrayDescriptor->link_phy = TD_TERMINATE;
182 	fStrayDescriptor->link_log = 0;
183 	fStrayDescriptor->buffer_phy = 0;
184 	fStrayDescriptor->buffer_log = 0;
185 	fStrayDescriptor->buffer_size = 0;
186 	fStrayDescriptor->token = TD_TOKEN_NULL_DATA
187 		| (0x7f << TD_TOKEN_DEVADDR_SHIFT) | TD_TOKEN_IN;
188 
189 	if (!Lock()) {
190 		fStack->FreeChunk(fStrayDescriptor, (void *)fStrayDescriptor->this_phy,
191 			sizeof(uhci_td));
192 		return B_ERROR;
193 	}
194 
195 	fQueueHead->link_phy = fStrayDescriptor->this_phy;
196 	fQueueHead->link_log = fStrayDescriptor;
197 	fQueueHead->element_phy = QH_TERMINATE;
198 	Unlock();
199 
200 	return B_OK;
201 }
202 
203 
204 status_t
205 Queue::AppendDescriptorChain(uhci_td *descriptor)
206 {
207 	TRACE(("usb_uhci: appending descriptor chain\n"));
208 
209 	if (!Lock())
210 		return B_ERROR;
211 
212 #ifdef TRACE_USB
213 	print_descriptor_chain(descriptor);
214 #endif
215 
216 	if (fQueueTop == NULL) {
217 		// the queue is empty, make this the first element
218 		fQueueTop = descriptor;
219 		fQueueHead->element_phy = descriptor->this_phy;
220 		TRACE(("usb_uhci: first transfer in queue\n"));
221 	} else {
222 		// there are transfers linked, append to the queue
223 		uhci_td *element = fQueueTop;
224 		while ((element->link_phy & TD_TERMINATE) == 0)
225 			element = (uhci_td *)element->link_log;
226 
227 		element->link_log = descriptor;
228 		element->link_phy = descriptor->this_phy | TD_DEPTH_FIRST;
229 		TRACE(("usb_uhci: appended transfer to queue\n"));
230 	}
231 
232 	Unlock();
233 	return B_OK;
234 }
235 
236 
237 status_t
238 Queue::RemoveDescriptorChain(uhci_td *firstDescriptor, uhci_td *lastDescriptor)
239 {
240 	TRACE(("usb_uhci: removing descriptor chain\n"));
241 
242 	if (!Lock())
243 		return B_ERROR;
244 
245 	if (fQueueTop == firstDescriptor) {
246 		// it is the first chain in this queue
247 		if ((lastDescriptor->link_phy & TD_TERMINATE) > 0) {
248 			// it is the only chain in this queue
249 			fQueueTop = NULL;
250 			fQueueHead->element_phy = QH_TERMINATE;
251 		} else {
252 			// there are still linked transfers
253 			fQueueTop = (uhci_td *)lastDescriptor->link_log;
254 			fQueueHead->element_phy = fQueueTop->this_phy & TD_LINK_MASK;
255 		}
256 	} else {
257 		// unlink the chain
258 		uhci_td *element = fQueueTop;
259 		while (element) {
260 			if (element->link_log == firstDescriptor) {
261 				element->link_log = lastDescriptor->link_log;
262 				element->link_phy = lastDescriptor->link_phy;
263 				break;
264 			}
265 
266 			element = (uhci_td *)element->link_log;
267 		}
268 
269 		element = firstDescriptor;
270 		while (element && element != lastDescriptor) {
271 			if ((fQueueHead->element_phy & TD_LINK_MASK) == element->this_phy) {
272 				fQueueHead->element_phy = lastDescriptor->link_phy;
273 				break;
274 			}
275 
276 			element = (uhci_td *)element->link_log;
277 		}
278 	}
279 
280 	lastDescriptor->link_log = NULL;
281 	lastDescriptor->link_phy = TD_TERMINATE;
282 
283 #ifdef TRACE_USB
284 	print_descriptor_chain(firstDescriptor);
285 #endif
286 
287 	Unlock();
288 	return B_OK;
289 }
290 
291 
292 addr_t
293 Queue::PhysicalAddress()
294 {
295 	return fQueueHead->this_phy;
296 }
297 
298 
299 void
300 Queue::PrintToStream()
301 {
302 #ifdef TRACE_USB
303 	dprintf("USB UHCI Queue:\n");
304 	dprintf("link phy: 0x%08lx; link type: %s; terminate: %s\n", fQueueHead->link_phy & 0xfff0, fQueueHead->link_phy & 0x0002 ? "QH" : "TD", fQueueHead->link_phy & 0x0001 ? "yes" : "no");
305 	dprintf("elem phy: 0x%08lx; elem type: %s; terminate: %s\n", fQueueHead->element_phy & 0xfff0, fQueueHead->element_phy & 0x0002 ? "QH" : "TD", fQueueHead->element_phy & 0x0001 ? "yes" : "no");
306 	dprintf("elements:\n");
307 	print_descriptor_chain(fQueueTop);
308 #endif
309 }
310 
311 
312 //
313 // #pragma mark -
314 //
315 
316 
317 UHCI::UHCI(pci_info *info, Stack *stack)
318 	:	BusManager(stack),
319 		fPCIInfo(info),
320 		fStack(stack),
321 		fFrameArea(-1),
322 		fFrameList(NULL),
323 		fQueueCount(0),
324 		fQueues(NULL),
325 		fFirstTransfer(NULL),
326 		fLastTransfer(NULL),
327 		fFinishThread(-1),
328 		fStopFinishThread(false),
329 		fRootHub(NULL),
330 		fRootHubAddress(0),
331 		fPortResetChange(0)
332 {
333 	if (!fInitOK) {
334 		TRACE_ERROR(("usb_uhci: bus manager failed to init\n"));
335 		return;
336 	}
337 
338 	TRACE(("usb_uhci: constructing new UHCI Host Controller Driver\n"));
339 	fInitOK = false;
340 
341 	fRegisterBase = sPCIModule->read_pci_config(fPCIInfo->bus,
342 		fPCIInfo->device, fPCIInfo->function, PCI_memory_base, 4);
343 	fRegisterBase &= PCI_address_io_mask;
344 	TRACE_ERROR(("usb_uhci: iospace offset: 0x%08lx\n", fRegisterBase));
345 
346 	if (fRegisterBase == 0) {
347 		fRegisterBase = fPCIInfo->u.h0.base_registers[0];
348 		TRACE_ERROR(("usb_uhci: register base: 0x%08lx\n", fRegisterBase));
349 	}
350 
351 	// enable pci address access
352 	uint16 command = PCI_command_io | PCI_command_master | PCI_command_memory;
353 	command |= sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device,
354 		fPCIInfo->function, PCI_command, 2);
355 
356 	sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
357 		fPCIInfo->function, PCI_command, 2, command);
358 
359 	// make sure we gain controll of the UHCI controller instead of the BIOS
360 	sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, 2,
361 		PCI_LEGSUP, 2, PCI_LEGSUP_USBPIRQDEN);
362 
363 	// disable interrupts
364 	WriteReg16(UHCI_USBINTR, 0);
365 
366 	// do a global and host reset
367 	GlobalReset();
368 	if (ControllerReset() < B_OK) {
369 		TRACE_ERROR(("usb_uhci: host failed to reset\n"));
370 		return;
371 	}
372 
373 	// Setup the frame list
374 	void *physicalAddress;
375 	fFrameArea = fStack->AllocateArea((void **)&fFrameList,
376 		(void **)&physicalAddress, 4096, "USB UHCI framelist");
377 
378 	if (fFrameArea < B_OK) {
379 		TRACE_ERROR(("usb_uhci: unable to create an area for the frame pointer list\n"));
380 		return;
381 	}
382 
383 	// Set base pointer and reset frame number
384 	WriteReg32(UHCI_FRBASEADD, (uint32)physicalAddress);
385 	WriteReg16(UHCI_FRNUM, 0);
386 
387 	fQueueCount = 4;
388 	fQueues = new(std::nothrow) Queue *[fQueueCount];
389 	if (!fQueues) {
390 		delete_area(fFrameArea);
391 		return;
392 	}
393 
394 	for (int32 i = 0; i < fQueueCount; i++) {
395 		fQueues[i] = new(std::nothrow) Queue(fStack);
396 		if (!fQueues[i] || fQueues[i]->InitCheck() < B_OK) {
397 			TRACE_ERROR(("usb_uhci: cannot create queues\n"));
398 			delete_area(fFrameArea);
399 			return;
400 		}
401 
402 		if (i > 0)
403 			fQueues[i - 1]->LinkTo(fQueues[i]);
404 	}
405 
406 	// Make sure the last queue terminates
407 	fQueues[fQueueCount - 1]->TerminateByStrayDescriptor();
408 
409 	for (int32 i = 0; i < 1024; i++)
410 		fFrameList[i] = fQueues[0]->PhysicalAddress() | FRAMELIST_NEXT_IS_QH;
411 
412 	// create semaphore the finisher thread will wait for
413 	fFinishTransfersSem = create_sem(0, "UHCI Finish Transfers");
414 	if (fFinishTransfersSem < B_OK) {
415 		TRACE_ERROR(("usb_uhci: failed to create semaphore\n"));
416 		return;
417 	}
418 
419 	// Create the finisher service thread
420 	fFinishThread = spawn_kernel_thread(FinishThread,
421 		"uhci finish thread", B_NORMAL_PRIORITY, (void *)this);
422 	resume_thread(fFinishThread);
423 
424 	// Install the interrupt handler
425 	TRACE(("usb_uhci: installing interrupt handler\n"));
426 	install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
427 		InterruptHandler, (void *)this, 0);
428 
429 	// Enable interrupts
430 	WriteReg16(UHCI_USBINTR, UHCI_USBINTR_CRC | UHCI_USBINTR_RESUME
431 		| UHCI_USBINTR_IOC | UHCI_USBINTR_SHORT);
432 
433 	TRACE(("usb_uhci: UHCI Host Controller Driver constructed\n"));
434 	fInitOK = true;
435 }
436 
437 
438 UHCI::~UHCI()
439 {
440 	int32 result = 0;
441 	fStopFinishThread = true;
442 	delete_sem(fFinishTransfersSem);
443 	wait_for_thread(fFinishThread, &result);
444 
445 	Lock();
446 	transfer_data *transfer = fFirstTransfer;
447 	while (transfer) {
448 		transfer_data *next = transfer->link;
449 		delete transfer;
450 		transfer = next;
451 	}
452 
453 	for (int32 i = 0; i < fQueueCount; i++)
454 		delete fQueues[i];
455 
456 	delete [] fQueues;
457 	delete fRootHub;
458 	delete_area(fFrameArea);
459 
460 	put_module(B_PCI_MODULE_NAME);
461 	Unlock();
462 }
463 
464 
465 status_t
466 UHCI::Start()
467 {
468 	// Start the host controller, then start the Busmanager
469 	TRACE(("usb_uhci: starting UHCI BusManager\n"));
470 	TRACE(("usb_uhci: usbcmd reg 0x%04x, usbsts reg 0x%04x\n",
471 		ReadReg16(UHCI_USBCMD), ReadReg16(UHCI_USBSTS)));
472 
473 	// Set the run bit in the command register
474 	WriteReg16(UHCI_USBCMD, ReadReg16(UHCI_USBCMD) | UHCI_USBCMD_RS);
475 
476 	bool running = false;
477 	for (int32 i = 0; i < 10; i++) {
478 		uint16 status = ReadReg16(UHCI_USBSTS);
479 		TRACE(("usb_uhci: current loop %ld, status 0x%04x\n", i, status));
480 
481 		if (status & UHCI_USBSTS_HCHALT)
482 			snooze(10000);
483 		else {
484 			running = true;
485 			break;
486 		}
487 	}
488 
489 	if (!running) {
490 		TRACE_ERROR(("usb_uhci: controller won't start running\n"));
491 		return B_ERROR;
492 	}
493 
494 	fRootHubAddress = AllocateAddress();
495 	fRootHub = new(std::nothrow) UHCIRootHub(RootObject(), fRootHubAddress);
496 	if (!fRootHub) {
497 		TRACE_ERROR(("usb_uhci: no memory to allocate root hub\n"));
498 		return B_NO_MEMORY;
499 	}
500 
501 	if (fRootHub->InitCheck() < B_OK) {
502 		TRACE_ERROR(("usb_uhci: root hub failed init check\n"));
503 		delete fRootHub;
504 		return B_ERROR;
505 	}
506 
507 	SetRootHub(fRootHub);
508 
509 	TRACE(("usb_uhci: controller is started. status: %u curframe: %u\n",
510 		ReadReg16(UHCI_USBSTS), ReadReg16(UHCI_FRNUM)));
511 	return BusManager::Start();
512 }
513 
514 
515 status_t
516 UHCI::SubmitTransfer(Transfer *transfer)
517 {
518 	// Short circuit the root hub
519 	if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress)
520 		return fRootHub->ProcessTransfer(this, transfer);
521 
522 	TRACE(("usb_uhci: submit transfer called for device %d\n", transfer->TransferPipe()->DeviceAddress()));
523 	if (transfer->TransferPipe()->Type() & USB_OBJECT_CONTROL_PIPE)
524 		return SubmitRequest(transfer);
525 
526 	if (transfer->VectorCount() == 0)
527 		return B_BAD_VALUE;
528 
529 	Pipe *pipe = transfer->TransferPipe();
530 	bool directionIn = (pipe->Direction() == Pipe::In);
531 
532 	uhci_td *firstDescriptor = NULL;
533 	uhci_td *lastDescriptor = NULL;
534 	status_t result = CreateDescriptorChain(pipe, &firstDescriptor,
535 		&lastDescriptor, directionIn ? TD_TOKEN_IN : TD_TOKEN_OUT,
536 		transfer->VectorLength());
537 
538 	if (result < B_OK)
539 		return result;
540 	if (!firstDescriptor || !lastDescriptor)
541 		return B_NO_MEMORY;
542 
543 	lastDescriptor->status |= TD_CONTROL_IOC;
544 	lastDescriptor->link_phy = TD_TERMINATE;
545 	lastDescriptor->link_log = 0;
546 
547 	if (!directionIn) {
548 		WriteDescriptorChain(firstDescriptor, transfer->Vector(),
549 			transfer->VectorCount());
550 	}
551 
552 	Queue *queue = fQueues[3];
553 	if (pipe->Type() & USB_OBJECT_INTERRUPT_PIPE)
554 		queue = fQueues[2];
555 
556 	result = AddPendingTransfer(transfer, queue, firstDescriptor,
557 		firstDescriptor, lastDescriptor, directionIn);
558 	if (result < B_OK) {
559 		TRACE_ERROR(("usb_uhci: failed to add pending transfer\n"));
560 		FreeDescriptorChain(firstDescriptor);
561 		return result;
562 	}
563 
564 	result = queue->AppendDescriptorChain(firstDescriptor);
565 	if (result < B_OK) {
566 		TRACE_ERROR(("usb_uhci: failed to append descriptor chain\n"));
567 		FreeDescriptorChain(firstDescriptor);
568 		return result;
569 	}
570 
571 	return B_OK;
572 }
573 
574 
575 status_t
576 UHCI::SubmitRequest(Transfer *transfer)
577 {
578 	Pipe *pipe = transfer->TransferPipe();
579 	usb_request_data *requestData = transfer->RequestData();
580 	bool directionIn = (requestData->RequestType & USB_REQTYPE_DEVICE_IN) > 0;
581 
582 	uhci_td *setupDescriptor = CreateDescriptor(pipe, TD_TOKEN_SETUP,
583 		sizeof(usb_request_data));
584 
585 	uhci_td *statusDescriptor = CreateDescriptor(pipe,
586 		directionIn ? TD_TOKEN_OUT : TD_TOKEN_IN, 0);
587 
588 	if (!setupDescriptor || !statusDescriptor) {
589 		TRACE_ERROR(("usb_uhci: failed to allocate descriptors\n"));
590 		FreeDescriptor(setupDescriptor);
591 		FreeDescriptor(statusDescriptor);
592 		return B_NO_MEMORY;
593 	}
594 
595 	iovec vector;
596 	vector.iov_base = requestData;
597 	vector.iov_len = sizeof(usb_request_data);
598 	WriteDescriptorChain(setupDescriptor, &vector, 1);
599 
600 	statusDescriptor->status |= TD_CONTROL_IOC;
601 	statusDescriptor->token |= TD_TOKEN_DATA1;
602 	statusDescriptor->link_phy = TD_TERMINATE;
603 	statusDescriptor->link_log = NULL;
604 
605 	uhci_td *dataDescriptor = NULL;
606 	if (transfer->VectorCount() > 0) {
607 		uhci_td *lastDescriptor = NULL;
608 		status_t result = CreateDescriptorChain(pipe, &dataDescriptor,
609 			&lastDescriptor, directionIn ? TD_TOKEN_IN : TD_TOKEN_OUT,
610 			transfer->VectorLength());
611 
612 		if (result < B_OK) {
613 			FreeDescriptor(setupDescriptor);
614 			FreeDescriptor(statusDescriptor);
615 			return result;
616 		}
617 
618 		if (!directionIn) {
619 			WriteDescriptorChain(dataDescriptor, transfer->Vector(),
620 				transfer->VectorCount());
621 		}
622 
623 		LinkDescriptors(setupDescriptor, dataDescriptor);
624 		LinkDescriptors(lastDescriptor, statusDescriptor);
625 	} else {
626 		// Link transfer and status descriptors directly
627 		LinkDescriptors(setupDescriptor, statusDescriptor);
628 	}
629 
630 	status_t result = AddPendingTransfer(transfer, fQueues[1], setupDescriptor,
631 		dataDescriptor, statusDescriptor, directionIn);
632 	if (result < B_OK) {
633 		TRACE_ERROR(("usb_uhci: failed to add pending transfer\n"));
634 		FreeDescriptorChain(setupDescriptor);
635 		return result;
636 	}
637 
638 	result = fQueues[1]->AppendDescriptorChain(setupDescriptor);
639 	if (result < B_OK) {
640 		TRACE_ERROR(("usb_uhci: failed to append descriptor chain\n"));
641 		FreeDescriptorChain(setupDescriptor);
642 		return result;
643 	}
644 
645 	return B_OK;
646 }
647 
648 
649 status_t
650 UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
651 	uhci_td *firstDescriptor, uhci_td *dataDescriptor, uhci_td *lastDescriptor,
652 	bool directionIn)
653 {
654 	transfer_data *data = new(std::nothrow) transfer_data();
655 	if (!data)
656 		return B_NO_MEMORY;
657 
658 	data->transfer = transfer;
659 	data->queue = queue;
660 	data->first_descriptor = firstDescriptor;
661 	data->data_descriptor = dataDescriptor;
662 	data->last_descriptor = lastDescriptor;
663 	data->user_area = -1;
664 	data->incoming = directionIn;
665 	data->link = NULL;
666 
667 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
668 	if (directionIn) {
669 		// we might need to access a buffer in userspace. this will not
670 		// be possible in the kernel space finisher thread unless we
671 		// get the proper area id for the space we need and then clone it
672 		// before writing to it. this is of course terribly inefficient...
673 		iovec *vector = transfer->Vector();
674 		size_t vectorCount = transfer->VectorCount();
675 		for (size_t i = 0; i < vectorCount; i++) {
676 			if (IS_USER_ADDRESS(vector[i].iov_base)) {
677 				data->user_area = area_for(vector[i].iov_base);
678 				if (data->user_area < B_OK) {
679 					TRACE_ERROR(("usb_uhci: failed to get area of userspace buffer\n"));
680 					delete data;
681 					return B_BAD_ADDRESS;
682 				}
683 
684 				break;
685 			}
686 		}
687 
688 		if (data->user_area >= B_OK) {
689 			area_info areaInfo;
690 			if (get_area_info(data->user_area, &areaInfo) < B_OK) {
691 				TRACE_ERROR(("usb_uhci: failed to get info about user area\n"));
692 				delete data;
693 				return B_BAD_ADDRESS;
694 			}
695 
696 			for (size_t i = 0; i < vectorCount; i++) {
697 				(uint8 *)vector[i].iov_base -= (uint8 *)areaInfo.address;
698 
699 				if ((size_t)vector[i].iov_base > areaInfo.size
700 					|| (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) {
701 					TRACE_ERROR(("usb_uhci: output data buffer spans across multiple areas!\n"));
702 					delete data;
703 					return B_BAD_ADDRESS;
704 				}
705 			}
706 		}
707 	}
708 #endif // !HAIKU_TARGET_PLATFORM_HAIKU
709 
710 	if (!Lock()) {
711 		delete data;
712 		return B_ERROR;
713 	}
714 
715 	if (fLastTransfer)
716 		fLastTransfer->link = data;
717 	else
718 		fFirstTransfer = data;
719 
720 	fLastTransfer = data;
721 	Unlock();
722 
723 	return B_OK;
724 }
725 
726 
727 int32
728 UHCI::FinishThread(void *data)
729 {
730 	((UHCI *)data)->FinishTransfers();
731 	return B_OK;
732 }
733 
734 
735 void
736 UHCI::FinishTransfers()
737 {
738 	while (!fStopFinishThread) {
739 		if (acquire_sem(fFinishTransfersSem) < B_OK)
740 			continue;
741 
742 		// eat up sems that have been released by multiple interrupts
743 		int32 semCount = 0;
744 		get_sem_count(fFinishTransfersSem, &semCount);
745 		if (semCount > 0)
746 			acquire_sem_etc(fFinishTransfersSem, semCount, B_RELATIVE_TIMEOUT, 0);
747 
748 		if (!Lock())
749 			continue;
750 
751 		TRACE(("usb_uhci: finishing transfers (first transfer: 0x%08lx; last transfer: 0x%08lx)\n", (uint32)fFirstTransfer, (uint32)fLastTransfer));
752 		transfer_data *lastTransfer = NULL;
753 		transfer_data *transfer = fFirstTransfer;
754 		Unlock();
755 
756 		while (transfer) {
757 			bool transferDone = false;
758 			uhci_td *descriptor = transfer->first_descriptor;
759 
760 			while (descriptor) {
761 				uint32 status = descriptor->status;
762 				if (status & TD_STATUS_ACTIVE) {
763 					TRACE(("usb_uhci: td (0x%08lx) still active\n", descriptor->this_phy));
764 					// still in progress
765 					break;
766 				}
767 
768 				if (status & TD_ERROR_MASK) {
769 					TRACE_ERROR(("usb_uhci: td (0x%08lx) error: 0x%08lx\n", descriptor->this_phy, status));
770 					// an error occured. we have to remove the
771 					// transfer from the queue and clean up
772 
773 					status_t callbackStatus = B_ERROR;
774 					uint8 errorCount = status >> TD_ERROR_COUNT_SHIFT;
775 					errorCount &= TD_ERROR_COUNT_MASK;
776 					if (errorCount == 0) {
777 						// the error counter counted down to zero, report why
778 						int32 reasons = 0;
779 						if (status & TD_STATUS_ERROR_BUFFER) {
780 							callbackStatus = transfer->incoming ? B_DEV_DATA_OVERRUN : B_DEV_DATA_UNDERRUN;
781 							reasons++;
782 						}
783 						if (status & TD_STATUS_ERROR_TIMEOUT) {
784 							callbackStatus = transfer->incoming ? B_DEV_CRC_ERROR : B_TIMED_OUT;
785 							reasons++;
786 						}
787 						if (status & TD_STATUS_ERROR_NAK) {
788 							callbackStatus = B_DEV_UNEXPECTED_PID;
789 							reasons++;
790 						}
791 						if (status & TD_STATUS_ERROR_BITSTUFF) {
792 							callbackStatus = B_DEV_CRC_ERROR;
793 							reasons++;
794 						}
795 
796 						if (reasons > 1)
797 							callbackStatus = B_DEV_MULTIPLE_ERRORS;
798 					} else if (status & TD_STATUS_ERROR_BABBLE) {
799 						// there is a babble condition
800 						callbackStatus = transfer->incoming ? B_DEV_FIFO_OVERRUN : B_DEV_FIFO_UNDERRUN;
801 					} else {
802 						// if the error counter didn't count down to zero
803 						// and there was no babble, then this halt was caused
804 						// by a stall handshake
805 						callbackStatus = B_DEV_STALLED;
806 					}
807 
808 					transfer->queue->RemoveDescriptorChain(
809 						transfer->first_descriptor,
810 						transfer->last_descriptor);
811 
812 					FreeDescriptorChain(transfer->first_descriptor);
813 					transfer->transfer->Finished(callbackStatus, 0);
814 					transferDone = true;
815 					break;
816 				}
817 
818 				// either all descriptors are done, or we have a short packet
819 				if (descriptor == transfer->last_descriptor
820 					|| (descriptor->status & TD_STATUS_ACTLEN_MASK)
821 					< (descriptor->token >> TD_TOKEN_MAXLEN_SHIFT)) {
822 					TRACE(("usb_uhci: td (0x%08lx) ok\n", descriptor->this_phy));
823 					// we got through without errors so we are finished
824 					transfer->queue->RemoveDescriptorChain(
825 						transfer->first_descriptor,
826 						transfer->last_descriptor);
827 
828 					size_t actualLength = 0;
829 					uint8 lastDataToggle = 0;
830 					if (transfer->data_descriptor && transfer->incoming) {
831 						// data to read out
832 						iovec *vector = transfer->transfer->Vector();
833 						size_t vectorCount = transfer->transfer->VectorCount();
834 
835 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
836 						area_id clonedArea = -1;
837 						if (transfer->user_area >= B_OK) {
838 							// we got a userspace output buffer, need to clone
839 							// the area for that space first and map the iovecs
840 							// to this cloned area.
841 							void *clonedMemory = NULL;
842 							clonedArea = clone_area("userspace accessor",
843 								&clonedMemory, B_ANY_ADDRESS,
844 								B_WRITE_AREA | B_KERNEL_WRITE_AREA,
845 								transfer->user_area);
846 
847 							for (size_t i = 0; i < vectorCount; i++)
848 								(uint8 *)vector[i].iov_base += (addr_t)clonedMemory;
849 						}
850 #endif // !HAIKU_TARGET_PLATFORM_HAIKU
851 
852 						actualLength = ReadDescriptorChain(
853 							transfer->data_descriptor,
854 							vector, vectorCount,
855 							&lastDataToggle);
856 
857 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
858 						if (clonedArea >= B_OK)
859 							delete_area(clonedArea);
860 #endif // !HAIKU_TARGET_PLATFORM_HAIKU
861 					} else {
862 						// read the actual length that was sent
863 						actualLength = ReadActualLength(
864 							transfer->first_descriptor, &lastDataToggle);
865 					}
866 
867 					FreeDescriptorChain(transfer->first_descriptor);
868 					transfer->transfer->TransferPipe()->SetDataToggle(lastDataToggle == 0);
869 					transfer->transfer->Finished(B_OK, actualLength);
870 					transferDone = true;
871 					break;
872 				}
873 
874 				descriptor = (uhci_td *)descriptor->link_log;
875 			}
876 
877 			if (transferDone) {
878 				if (Lock()) {
879 					if (lastTransfer)
880 						lastTransfer->link = transfer->link;
881 
882 					if (transfer == fFirstTransfer)
883 						fFirstTransfer = transfer->link;
884 					if (transfer == fLastTransfer)
885 						fLastTransfer = lastTransfer;
886 
887 					transfer_data *next = transfer->link;
888 					delete transfer->transfer;
889 					delete transfer;
890 					transfer = next;
891 
892 					Unlock();
893 				}
894 			} else {
895 				lastTransfer = transfer;
896 				transfer = transfer->link;
897 			}
898 		}
899 	}
900 }
901 
902 
903 void
904 UHCI::GlobalReset()
905 {
906 	uint8 sofValue = ReadReg8(UHCI_SOFMOD);
907 
908 	WriteReg16(UHCI_USBCMD, UHCI_USBCMD_GRESET);
909 	snooze(100000);
910 	WriteReg16(UHCI_USBCMD, 0);
911 	snooze(10000);
912 
913 	WriteReg8(UHCI_SOFMOD, sofValue);
914 }
915 
916 
917 status_t
918 UHCI::ControllerReset()
919 {
920 	WriteReg16(UHCI_USBCMD, UHCI_USBCMD_HCRESET);
921 
922 	int32 tries = 5;
923 	while (ReadReg16(UHCI_USBCMD) & UHCI_USBCMD_HCRESET) {
924 		snooze(10000);
925 		if (tries-- < 0)
926 			return B_ERROR;
927 	}
928 
929 	return B_OK;
930 }
931 
932 
933 status_t
934 UHCI::GetPortStatus(uint8 index, usb_port_status *status)
935 {
936 	if (index > 1)
937 		return B_BAD_INDEX;
938 
939 	status->status = status->change = 0;
940 	uint16 portStatus = ReadReg16(UHCI_PORTSC1 + index * 2);
941 
942 	// build the status
943 	if (portStatus & UHCI_PORTSC_CURSTAT)
944 		status->status |= PORT_STATUS_CONNECTION;
945 	if (portStatus & UHCI_PORTSC_ENABLED)
946 		status->status |= PORT_STATUS_ENABLE;
947 	if (portStatus & UHCI_PORTSC_RESET)
948 		status->status |= PORT_STATUS_RESET;
949 	if (portStatus & UHCI_PORTSC_LOWSPEED)
950 		status->status |= PORT_STATUS_LOW_SPEED;
951 
952 	// build the change
953 	if (portStatus & UHCI_PORTSC_STATCHA)
954 		status->change |= PORT_STATUS_CONNECTION;
955 	if (portStatus & UHCI_PORTSC_ENABCHA)
956 		status->change |= PORT_STATUS_ENABLE;
957 
958 	// ToDo: work out suspended/resume
959 
960 	// there are no bits to indicate reset change
961 	if (fPortResetChange & (1 << index))
962 		status->change |= PORT_STATUS_RESET;
963 
964 	// the port is automagically powered on
965 	status->status |= PORT_STATUS_POWER;
966 	return B_OK;
967 }
968 
969 
970 status_t
971 UHCI::SetPortFeature(uint8 index, uint16 feature)
972 {
973 	if (index > 1)
974 		return B_BAD_INDEX;
975 
976 	switch (feature) {
977 		case PORT_RESET:
978 			return ResetPort(index);
979 
980 		case PORT_POWER:
981 			// the ports are automatically powered
982 			return B_OK;
983 	}
984 
985 	return B_BAD_VALUE;
986 }
987 
988 
989 status_t
990 UHCI::ClearPortFeature(uint8 index, uint16 feature)
991 {
992 	if (index > 1)
993 		return B_BAD_INDEX;
994 
995 	uint32 portRegister = UHCI_PORTSC1 + index * 2;
996 	uint16 portStatus = ReadReg16(portRegister) & UHCI_PORTSC_DATAMASK;
997 
998 	switch (feature) {
999 		case C_PORT_RESET:
1000 			fPortResetChange &= ~(1 << index);
1001 			return B_OK;
1002 
1003 		case C_PORT_CONNECTION:
1004 			WriteReg16(portRegister, portStatus | UHCI_PORTSC_STATCHA);
1005 			return B_OK;
1006 	}
1007 
1008 	return B_BAD_VALUE;
1009 }
1010 
1011 
1012 status_t
1013 UHCI::ResetPort(uint8 index)
1014 {
1015 	if (index > 1)
1016 		return B_BAD_INDEX;
1017 
1018 	TRACE(("usb_uhci: reset port %d\n", index));
1019 
1020 	uint32 port = UHCI_PORTSC1 + index * 2;
1021 	uint16 status = ReadReg16(port);
1022 	status &= UHCI_PORTSC_DATAMASK;
1023 	WriteReg16(port, status | UHCI_PORTSC_RESET);
1024 	snooze(250000);
1025 
1026 	status = ReadReg16(port);
1027 	status &= UHCI_PORTSC_DATAMASK;
1028 	WriteReg16(port, status & ~UHCI_PORTSC_RESET);
1029 	snooze(1000);
1030 
1031 	for (int32 i = 10; i > 0; i--) {
1032 		// try to enable the port
1033 		status = ReadReg16(port);
1034 		status &= UHCI_PORTSC_DATAMASK;
1035 		WriteReg16(port, status | UHCI_PORTSC_ENABLED);
1036 		snooze(50000);
1037 
1038 		status = ReadReg16(port);
1039 
1040 		if ((status & UHCI_PORTSC_CURSTAT) == 0) {
1041 			// no device connected. since we waited long enough we can assume
1042 			// that the port was reset and no device is connected.
1043 			break;
1044 		}
1045 
1046 		if (status & (UHCI_PORTSC_STATCHA | UHCI_PORTSC_ENABCHA)) {
1047 			// port enabled changed or connection status were set.
1048 			// acknowledge either / both and wait again.
1049 			status &= UHCI_PORTSC_DATAMASK;
1050 			WriteReg16(port, status | UHCI_PORTSC_STATCHA | UHCI_PORTSC_ENABCHA);
1051 			continue;
1052 		}
1053 
1054 		if (status & UHCI_PORTSC_ENABLED) {
1055 			// the port is enabled
1056 			break;
1057 		}
1058 	}
1059 
1060 	fPortResetChange |= (1 << index);
1061 	TRACE(("usb_uhci: port was reset: 0x%04x\n", ReadReg16(port)));
1062 	return B_OK;
1063 }
1064 
1065 
1066 int32
1067 UHCI::InterruptHandler(void *data)
1068 {
1069 	return ((UHCI *)data)->Interrupt();
1070 }
1071 
1072 
1073 int32
1074 UHCI::Interrupt()
1075 {
1076 	spinlock lock = 0;
1077 	acquire_spinlock(&lock);
1078 
1079 	// Check if we really had an interrupt
1080 	uint16 status = ReadReg16(UHCI_USBSTS);
1081 	if ((status & UHCI_INTERRUPT_MASK) == 0) {
1082 		release_spinlock(&lock);
1083 		return B_UNHANDLED_INTERRUPT;
1084 	}
1085 
1086 	uint16 acknowledge = 0;
1087 	bool finishTransfers = false;
1088 	int32 result = B_HANDLED_INTERRUPT;
1089 
1090 	if (status & UHCI_USBSTS_USBINT) {
1091 		TRACE(("usb_uhci: transfer finished\n"));
1092 		acknowledge |= UHCI_USBSTS_USBINT;
1093 		result = B_INVOKE_SCHEDULER;
1094 		finishTransfers = true;
1095 	}
1096 
1097 	if (status & UHCI_USBSTS_ERRINT) {
1098 		TRACE(("usb_uhci: transfer error\n"));
1099 		acknowledge |= UHCI_USBSTS_ERRINT;
1100 		result = B_INVOKE_SCHEDULER;
1101 		finishTransfers = true;
1102 	}
1103 
1104 	if (status & UHCI_USBSTS_RESDET) {
1105 		TRACE(("usb_uhci: resume detected\n"));
1106 		acknowledge |= UHCI_USBSTS_RESDET;
1107 	}
1108 
1109 	if (status & UHCI_USBSTS_HOSTERR) {
1110 		TRACE(("usb_uhci: host system error\n"));
1111 		acknowledge |= UHCI_USBSTS_HOSTERR;
1112 	}
1113 
1114 	if (status & UHCI_USBSTS_HCPRERR) {
1115 		TRACE(("usb_uhci: process error\n"));
1116 		acknowledge |= UHCI_USBSTS_HCPRERR;
1117 	}
1118 
1119 	if (status & UHCI_USBSTS_HCHALT) {
1120 		TRACE(("usb_uhci: host controller halted\n"));
1121 		// ToDo: cancel all transfers and reset the host controller
1122 		// acknowledge not needed
1123 	}
1124 
1125 	if (acknowledge)
1126 		WriteReg16(UHCI_USBSTS, acknowledge);
1127 
1128 	release_spinlock(&lock);
1129 
1130 	if (finishTransfers)
1131 		release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE);
1132 
1133 	return result;
1134 }
1135 
1136 
1137 status_t
1138 UHCI::AddTo(Stack *stack)
1139 {
1140 #ifdef TRACE_USB
1141 	set_dprintf_enabled(true);
1142 	load_driver_symbols("uhci");
1143 #endif
1144 
1145 	if (!sPCIModule) {
1146 		status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&sPCIModule);
1147 		if (status < B_OK) {
1148 			TRACE_ERROR(("usb_uhci: AddTo(): getting pci module failed! 0x%08lx\n",
1149 				status));
1150 			return status;
1151 		}
1152 	}
1153 
1154 	TRACE(("usb_uhci: AddTo(): setting up hardware\n"));
1155 
1156 	bool found = false;
1157 	pci_info *item = new(std::nothrow) pci_info;
1158 	if (!item) {
1159 		sPCIModule = NULL;
1160 		put_module(B_PCI_MODULE_NAME);
1161 		return B_NO_MEMORY;
1162 	}
1163 
1164 	for (int32 i = 0; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) {
1165 
1166 		if (item->class_base == PCI_serial_bus && item->class_sub == PCI_usb
1167 			&& item->class_api == PCI_usb_uhci) {
1168 			if (item->u.h0.interrupt_line == 0
1169 				|| item->u.h0.interrupt_line == 0xFF) {
1170 				TRACE_ERROR(("usb_uhci: AddTo(): found with invalid IRQ - check IRQ assignement\n"));
1171 				continue;
1172 			}
1173 
1174 			TRACE(("usb_uhci: AddTo(): found at IRQ %u\n", item->u.h0.interrupt_line));
1175 			UHCI *bus = new(std::nothrow) UHCI(item, stack);
1176 			if (!bus) {
1177 				delete item;
1178 				sPCIModule = NULL;
1179 				put_module(B_PCI_MODULE_NAME);
1180 				return B_NO_MEMORY;
1181 			}
1182 
1183 			if (bus->InitCheck() < B_OK) {
1184 				TRACE_ERROR(("usb_uhci: AddTo(): InitCheck() failed 0x%08lx\n", bus->InitCheck()));
1185 				delete bus;
1186 				continue;
1187 			}
1188 
1189 			// the bus took it away
1190 			item = new(std::nothrow) pci_info;
1191 
1192 			bus->Start();
1193 			stack->AddBusManager(bus);
1194 			found = true;
1195 		}
1196 	}
1197 
1198 	if (!found) {
1199 		TRACE_ERROR(("usb_uhci: no devices found\n"));
1200 		delete item;
1201 		sPCIModule = NULL;
1202 		put_module(B_PCI_MODULE_NAME);
1203 		return ENODEV;
1204 	}
1205 
1206 	delete item;
1207 	return B_OK;
1208 }
1209 
1210 
1211 uhci_td *
1212 UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, size_t bufferSize)
1213 {
1214 	uhci_td *result;
1215 	void *physicalAddress;
1216 
1217 	if (fStack->AllocateChunk((void **)&result, &physicalAddress,
1218 		sizeof(uhci_td)) < B_OK) {
1219 		TRACE_ERROR(("usb_uhci: failed to allocate a transfer descriptor\n"));
1220 		return NULL;
1221 	}
1222 
1223 	result->this_phy = (addr_t)physicalAddress;
1224 	result->status = TD_STATUS_ACTIVE | TD_CONTROL_3_ERRORS | TD_CONTROL_SPD;
1225 	if (pipe->Speed() == USB_SPEED_LOWSPEED)
1226 		result->status |= TD_CONTROL_LOWSPEED;
1227 
1228 	result->buffer_size = bufferSize;
1229 	if (bufferSize == 0)
1230 		result->token = TD_TOKEN_NULL_DATA;
1231 	else
1232 		result->token = (bufferSize - 1) << TD_TOKEN_MAXLEN_SHIFT;
1233 
1234 	result->token |= (pipe->EndpointAddress() << TD_TOKEN_ENDPTADDR_SHIFT)
1235 		| (pipe->DeviceAddress() << 8) | direction;
1236 
1237 	result->link_phy = 0;
1238 	result->link_log = NULL;
1239 	if (bufferSize <= 0) {
1240 		result->buffer_log = NULL;
1241 		result->buffer_phy = NULL;
1242 		return result;
1243 	}
1244 
1245 	if (fStack->AllocateChunk(&result->buffer_log, &result->buffer_phy,
1246 		bufferSize) < B_OK) {
1247 		TRACE_ERROR(("usb_uhci: unable to allocate space for the buffer\n"));
1248 		fStack->FreeChunk(result, (void *)result->this_phy, sizeof(uhci_td));
1249 		return NULL;
1250 	}
1251 
1252 	return result;
1253 }
1254 
1255 
1256 status_t
1257 UHCI::CreateDescriptorChain(Pipe *pipe, uhci_td **_firstDescriptor,
1258 	uhci_td **_lastDescriptor, uint8 direction, size_t bufferSize)
1259 {
1260 	size_t packetSize = pipe->MaxPacketSize();
1261 	int32 descriptorCount = (bufferSize + packetSize - 1) / packetSize;
1262 
1263 	bool dataToggle = pipe->DataToggle();
1264 	uhci_td *firstDescriptor = NULL;
1265 	uhci_td *lastDescriptor = *_firstDescriptor;
1266 	for (int32 i = 0; i < descriptorCount; i++) {
1267 		uhci_td *descriptor = CreateDescriptor(pipe, direction,
1268 			min_c(packetSize, bufferSize));
1269 
1270 		if (!descriptor) {
1271 			FreeDescriptorChain(firstDescriptor);
1272 			return B_NO_MEMORY;
1273 		}
1274 
1275 		if (dataToggle)
1276 			descriptor->token |= TD_TOKEN_DATA1;
1277 
1278 		// link to previous
1279 		if (lastDescriptor)
1280 			LinkDescriptors(lastDescriptor, descriptor);
1281 
1282 		dataToggle = !dataToggle;
1283 		bufferSize -= packetSize;
1284 		lastDescriptor = descriptor;
1285 		if (!firstDescriptor)
1286 			firstDescriptor = descriptor;
1287 	}
1288 
1289 	*_firstDescriptor = firstDescriptor;
1290 	*_lastDescriptor = lastDescriptor;
1291 	return B_OK;
1292 }
1293 
1294 
1295 void
1296 UHCI::FreeDescriptor(uhci_td *descriptor)
1297 {
1298 	if (!descriptor)
1299 		return;
1300 
1301 	if (descriptor->buffer_log) {
1302 		fStack->FreeChunk(descriptor->buffer_log,
1303 			(void *)descriptor->buffer_phy, descriptor->buffer_size);
1304 	}
1305 
1306 	fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, sizeof(uhci_td));
1307 }
1308 
1309 
1310 void
1311 UHCI::FreeDescriptorChain(uhci_td *topDescriptor)
1312 {
1313 	uhci_td *current = topDescriptor;
1314 	uhci_td *next = NULL;
1315 
1316 	while (current) {
1317 		next = (uhci_td *)current->link_log;
1318 		FreeDescriptor(current);
1319 		current = next;
1320 	}
1321 }
1322 
1323 
1324 void
1325 UHCI::LinkDescriptors(uhci_td *first, uhci_td *second)
1326 {
1327 	first->link_phy = second->this_phy | TD_DEPTH_FIRST;
1328 	first->link_log = second;
1329 }
1330 
1331 
1332 size_t
1333 UHCI::WriteDescriptorChain(uhci_td *topDescriptor, iovec *vector,
1334 	size_t vectorCount)
1335 {
1336 	uhci_td *current = topDescriptor;
1337 	size_t actualLength = 0;
1338 	size_t vectorIndex = 0;
1339 	size_t vectorOffset = 0;
1340 	size_t bufferOffset = 0;
1341 
1342 	while (current) {
1343 		if (!current->buffer_log)
1344 			break;
1345 
1346 		while (true) {
1347 			size_t length = min_c(current->buffer_size - bufferOffset,
1348 				vector[vectorIndex].iov_len - vectorOffset);
1349 
1350 			TRACE(("usb_uhci: copying %ld bytes to bufferOffset %ld from vectorOffset %ld at index %ld of %ld\n", length, bufferOffset, vectorOffset, vectorIndex, vectorCount));
1351 			memcpy((uint8 *)current->buffer_log + bufferOffset,
1352 				(uint8 *)vector[vectorIndex].iov_base + vectorOffset, length);
1353 
1354 			actualLength += length;
1355 			vectorOffset += length;
1356 			bufferOffset += length;
1357 
1358 			if (vectorOffset >= vector[vectorIndex].iov_len) {
1359 				if (++vectorIndex >= vectorCount) {
1360 					TRACE(("usb_uhci: wrote descriptor chain (%ld bytes, no more vectors)\n", actualLength));
1361 					return actualLength;
1362 				}
1363 
1364 				vectorOffset = 0;
1365 			}
1366 
1367 			if (bufferOffset >= current->buffer_size) {
1368 				bufferOffset = 0;
1369 				break;
1370 			}
1371 		}
1372 
1373 		if (current->link_phy & TD_TERMINATE)
1374 			break;
1375 
1376 		current = (uhci_td *)current->link_log;
1377 	}
1378 
1379 	TRACE(("usb_uhci: wrote descriptor chain (%ld bytes)\n", actualLength));
1380 	return actualLength;
1381 }
1382 
1383 
1384 size_t
1385 UHCI::ReadDescriptorChain(uhci_td *topDescriptor, iovec *vector,
1386 	size_t vectorCount, uint8 *lastDataToggle)
1387 {
1388 	uint8 dataToggle = 0;
1389 	uhci_td *current = topDescriptor;
1390 	size_t actualLength = 0;
1391 	size_t vectorIndex = 0;
1392 	size_t vectorOffset = 0;
1393 	size_t bufferOffset = 0;
1394 
1395 	while (current && (current->status & TD_STATUS_ACTIVE) == 0) {
1396 		if (!current->buffer_log)
1397 			break;
1398 
1399 		dataToggle = (current->token >> TD_TOKEN_DATA_TOGGLE_SHIFT) & 0x01;
1400 		size_t bufferSize = (current->status & TD_STATUS_ACTLEN_MASK) + 1;
1401 		if (bufferSize == TD_STATUS_ACTLEN_NULL + 1)
1402 			bufferSize = 0;
1403 
1404 		while (true) {
1405 			size_t length = min_c(bufferSize - bufferOffset,
1406 				vector[vectorIndex].iov_len - vectorOffset);
1407 
1408 			TRACE(("usb_uhci: copying %ld bytes to vectorOffset %ld from bufferOffset %ld at index %ld of %ld\n", length, vectorOffset, bufferOffset, vectorIndex, vectorCount));
1409 			memcpy((uint8 *)vector[vectorIndex].iov_base + vectorOffset,
1410 				(uint8 *)current->buffer_log + bufferOffset, length);
1411 
1412 			actualLength += length;
1413 			vectorOffset += length;
1414 			bufferOffset += length;
1415 
1416 			if (vectorOffset >= vector[vectorIndex].iov_len) {
1417 				if (++vectorIndex >= vectorCount) {
1418 					TRACE(("usb_uhci: read descriptor chain (%ld bytes, no more vectors)\n", actualLength));
1419 					if (lastDataToggle)
1420 						*lastDataToggle = dataToggle;
1421 					return actualLength;
1422 				}
1423 
1424 				vectorOffset = 0;
1425 			}
1426 
1427 			if (bufferOffset >= bufferSize) {
1428 				bufferOffset = 0;
1429 				break;
1430 			}
1431 		}
1432 
1433 		if (current->link_phy & TD_TERMINATE)
1434 			break;
1435 
1436 		current = (uhci_td *)current->link_log;
1437 	}
1438 
1439 	if (lastDataToggle)
1440 		*lastDataToggle = dataToggle;
1441 
1442 	TRACE(("usb_uhci: read descriptor chain (%ld bytes)\n", actualLength));
1443 	return actualLength;
1444 }
1445 
1446 
1447 size_t
1448 UHCI::ReadActualLength(uhci_td *topDescriptor, uint8 *lastDataToggle)
1449 {
1450 	size_t actualLength = 0;
1451 	uhci_td *current = topDescriptor;
1452 	uint8 dataToggle = 0;
1453 
1454 	while (current && (current->status & TD_STATUS_ACTIVE) == 0) {
1455 		size_t length = (current->status & TD_STATUS_ACTLEN_MASK) + 1;
1456 		if (length == TD_STATUS_ACTLEN_NULL + 1)
1457 			length = 0;
1458 
1459 		actualLength += length;
1460 		dataToggle = (current->token >> TD_TOKEN_DATA_TOGGLE_SHIFT) & 0x01;
1461 
1462 		if (current->link_phy & TD_TERMINATE)
1463 			break;
1464 
1465 		current = (uhci_td *)current->link_log;
1466 	}
1467 
1468 	if (lastDataToggle)
1469 		*lastDataToggle = dataToggle;
1470 
1471 	TRACE(("usb_uhci: read actual length (%ld bytes)\n", actualLength));
1472 	return actualLength;
1473 }
1474 
1475 
1476 inline void
1477 UHCI::WriteReg8(uint32 reg, uint8 value)
1478 {
1479 	sPCIModule->write_io_8(fRegisterBase + reg, value);
1480 }
1481 
1482 
1483 inline void
1484 UHCI::WriteReg16(uint32 reg, uint16 value)
1485 {
1486 	sPCIModule->write_io_16(fRegisterBase + reg, value);
1487 }
1488 
1489 
1490 inline void
1491 UHCI::WriteReg32(uint32 reg, uint32 value)
1492 {
1493 	sPCIModule->write_io_32(fRegisterBase + reg, value);
1494 }
1495 
1496 
1497 inline uint8
1498 UHCI::ReadReg8(uint32 reg)
1499 {
1500 	return sPCIModule->read_io_8(fRegisterBase + reg);
1501 }
1502 
1503 
1504 inline uint16
1505 UHCI::ReadReg16(uint32 reg)
1506 {
1507 	return sPCIModule->read_io_16(fRegisterBase + reg);
1508 }
1509 
1510 
1511 inline uint32
1512 UHCI::ReadReg32(uint32 reg)
1513 {
1514 	return sPCIModule->read_io_32(fRegisterBase + reg);
1515 }
1516