diff --git a/headers/os/drivers/USB_spec.h b/headers/os/drivers/USB_spec.h index 08fe58867f..9b94857370 100644 --- a/headers/os/drivers/USB_spec.h +++ b/headers/os/drivers/USB_spec.h @@ -62,6 +62,19 @@ #define USB_ENDPOINT_ATTR_INTERRUPT 0x03 #define USB_ENDPOINT_ATTR_MASK 0x03 +/* Synchronization - isochrnous endpoints only */ +#define USB_ENDPOINT_ATTR_NO_SYNCHRONIZE 0x00 +#define USB_ENDPOINT_ATTR_ASYNCRONOUS 0x04 +#define USB_ENDPOINT_ATTR_ADAPTIVE_SYNCHRO 0x08 +#define USB_ENDPOINT_ATTR_SYNCHRONOUS 0x0C +#define USB_ENDPOINT_ATTR_SYNCHRONIZE_MASK 0x0C + +/* Usage Type - isochrnous endpoints only */ +#define USB_ENDPOINT_ATTR_DATA_USAGE 0x00 +#define USB_ENDPOINT_ATTR_FEEDBACK_USAGE 0x10 +#define USB_ENDPOINT_ATTR_IMPLICIT_USAGE 0x20 +#define USB_ENDPOINT_ATTR_USAGE_MASK 0x30 + #define USB_ENDPOINT_ADDR_DIR_IN 0x80 #define USB_ENDPOINT_ADDR_DIR_OUT 0x00 diff --git a/src/add-ons/kernel/busses/usb/ohci.cpp b/src/add-ons/kernel/busses/usb/ohci.cpp index 3a28a49071..489a7627bb 100644 --- a/src/add-ons/kernel/busses/usb/ohci.cpp +++ b/src/add-ons/kernel/busses/usb/ohci.cpp @@ -1,11 +1,12 @@ /* - * Copyright 2005-2008, Haiku Inc. All rights reserved. + * Copyright 2005-2013, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Jan-Rixt Van Hoye * Salvatore Benedetto * Michael Lotz + * Siarzhuk Zharski */ #include @@ -74,6 +75,7 @@ OHCI::OHCI(pci_info *info, Stack *stack) fFinishThread(-1), fStopFinishThread(false), fProcessingPipe(NULL), + fFrameBandwidth(NULL), fRootHub(NULL), fRootHubAddress(0), fPortCount(0), @@ -312,6 +314,12 @@ OHCI::OHCI(pci_info *info, Stack *stack) fPortCount = numberOfPorts; TRACE("port count is %d\n", fPortCount); + // Create the array that will keep bandwidth information + fFrameBandwidth = new(std::nothrow) uint16[NUMBER_OF_FRAMES]; + + for (int32 i = 0; i < NUMBER_OF_FRAMES; i++) + fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH; + // Create semaphore the finisher thread will wait for fFinishTransfersSem = create_sem(0, "OHCI Finish Transfers"); if (fFinishTransfersSem < B_OK) { @@ -378,6 +386,7 @@ OHCI::~OHCI() _FreeEndpoint(fInterruptEndpoints[i]); } + delete [] fFrameBandwidth; delete [] fInterruptEndpoints; delete fRootHub; @@ -459,9 +468,6 @@ OHCI::SubmitTransfer(Transfer *transfer) status_t OHCI::CancelQueuedTransfers(Pipe *pipe, bool force) { - if (pipe->Type() & USB_OBJECT_ISO_PIPE) - return _CancelQueuedIsochronousTransfers(pipe, force); - if (!Lock()) return B_ERROR; @@ -487,6 +493,25 @@ OHCI::CancelQueuedTransfers(Pipe *pipe, bool force) = current->endpoint->tail_physical_descriptor; if (!force) { + if (pipe->Type() & USB_OBJECT_ISO_PIPE) { + ohci_isochronous_td *descriptor + = (ohci_isochronous_td *)current->first_descriptor; + while (descriptor) { + uint16 frame = OHCI_ITD_GET_STARTING_FRAME( + descriptor->flags); + _ReleaseIsochronousBandwidth(frame, + OHCI_ITD_GET_FRAME_COUNT(descriptor->flags)); + if (descriptor + == (ohci_isochronous_td*)current->last_descriptor) + // this is the last ITD of the transfer + break; + + descriptor + = (ohci_isochronous_td *) + descriptor->next_done_descriptor; + } + } + // If the transfer is canceled by force, the one causing the // cancel is probably not the one who initiated the transfer // and the callback is likely not safe anymore @@ -914,10 +939,55 @@ OHCI::_AddPendingTransfer(Transfer *transfer, status_t -OHCI::_CancelQueuedIsochronousTransfers(Pipe *pipe, bool force) +OHCI::_AddPendingIsochronousTransfer(Transfer *transfer, + ohci_endpoint_descriptor *endpoint, ohci_isochronous_td *firstDescriptor, + ohci_isochronous_td *lastDescriptor, bool directionIn) { - // TODO - return B_ERROR; + if (!transfer || !endpoint || !lastDescriptor) + return B_BAD_VALUE; + + transfer_data *data = new(std::nothrow) transfer_data; + if (!data) + return B_NO_MEMORY; + + status_t result = transfer->InitKernelAccess(); + if (result < B_OK) { + delete data; + return result; + } + + data->transfer = transfer; + data->endpoint = endpoint; + data->incoming = directionIn; + data->canceled = false; + data->link = NULL; + + // the current tail will become the first descriptor + data->first_descriptor = (ohci_general_td*)endpoint->tail_logical_descriptor; + + // the data and first descriptors are the same + data->data_descriptor = data->first_descriptor; + + // the last and the first descriptor might be the same + if (lastDescriptor == firstDescriptor) + data->last_descriptor = data->first_descriptor; + else + data->last_descriptor = (ohci_general_td*)lastDescriptor; + + if (!Lock()) { + delete data; + return B_ERROR; + } + + if (fLastTransfer) + fLastTransfer->link = data; + else + fFirstTransfer = data; + + fLastTransfer = data; + Unlock(); + + return B_OK; } @@ -957,10 +1027,20 @@ OHCI::_FinishTransfers() ohci_endpoint_descriptor *endpoint = transfer->endpoint; status_t callbackStatus = B_OK; + if (endpoint->flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT) { + transfer_data *next = transfer->link; + if (_FinishIsochronousTransfer(transfer, &lastTransfer)) { + delete transfer->transfer; + delete transfer; + } + transfer = next; + continue; + } + MutexLocker endpointLocker(endpoint->lock); if ((endpoint->head_physical_descriptor & OHCI_ENDPOINT_HEAD_MASK) - != endpoint->tail_physical_descriptor) { + != endpoint->tail_physical_descriptor) { // there are still active transfers on this endpoint, we need // to wait for all of them to complete, otherwise we'd read // a potentially bogus data toggle value below @@ -991,49 +1071,7 @@ OHCI::_FinishTransfers() // still ensures that this td was handled before). TRACE_ERROR("td error: 0x%08" B_PRIx32 "\n", status); - switch (status) { - case OHCI_TD_CONDITION_CRC_ERROR: - case OHCI_TD_CONDITION_BIT_STUFFING: - case OHCI_TD_CONDITION_TOGGLE_MISMATCH: - callbackStatus = B_DEV_CRC_ERROR; - break; - - case OHCI_TD_CONDITION_STALL: - callbackStatus = B_DEV_STALLED; - break; - - case OHCI_TD_CONDITION_NO_RESPONSE: - callbackStatus = B_TIMED_OUT; - break; - - case OHCI_TD_CONDITION_PID_CHECK_FAILURE: - callbackStatus = B_DEV_BAD_PID; - break; - - case OHCI_TD_CONDITION_UNEXPECTED_PID: - callbackStatus = B_DEV_UNEXPECTED_PID; - break; - - case OHCI_TD_CONDITION_DATA_OVERRUN: - callbackStatus = B_DEV_DATA_OVERRUN; - break; - - case OHCI_TD_CONDITION_DATA_UNDERRUN: - callbackStatus = B_DEV_DATA_UNDERRUN; - break; - - case OHCI_TD_CONDITION_BUFFER_OVERRUN: - callbackStatus = B_DEV_FIFO_OVERRUN; - break; - - case OHCI_TD_CONDITION_BUFFER_UNDERRUN: - callbackStatus = B_DEV_FIFO_UNDERRUN; - break; - - default: - callbackStatus = B_ERROR; - break; - } + callbackStatus = _GetStatusOfConditionCode(status); transferDone = true; break; @@ -1163,6 +1201,141 @@ OHCI::_FinishTransfers() } +bool +OHCI::_FinishIsochronousTransfer(transfer_data *transfer, + transfer_data **_lastTransfer) +{ + status_t callbackStatus = B_OK; + size_t actualLength = 0; + uint32 packet = 0; + + if (transfer->canceled) + callbackStatus = B_CANCELED; + else { + // at first check if ALL ITDs are retired by HC + ohci_isochronous_td *descriptor + = (ohci_isochronous_td *)transfer->first_descriptor; + while (descriptor) { + if (OHCI_TD_GET_CONDITION_CODE(descriptor->flags) + == OHCI_TD_CONDITION_NOT_ACCESSED) { + TRACE("ITD %p still active\n", descriptor); + *_lastTransfer = transfer; + return false; + } + + if (descriptor == (ohci_isochronous_td*)transfer->last_descriptor) { + // this is the last ITD of the transfer + descriptor = (ohci_isochronous_td *)transfer->first_descriptor; + break; + } + + descriptor + = (ohci_isochronous_td *)descriptor->next_done_descriptor; + } + + while (descriptor) { + uint32 status = OHCI_TD_GET_CONDITION_CODE(descriptor->flags); + if (status != OHCI_TD_CONDITION_NO_ERROR) { + TRACE_ERROR("ITD error: 0x%08" B_PRIx32 "\n", status); + // spec says that in most cases condition code + // of retired ITDs is set to NoError, but for the + // time overrun it can be DataOverrun. We assume + // the _first_ occurience of such error as status + // reported to the callback + if (callbackStatus == B_OK) + callbackStatus = _GetStatusOfConditionCode(status); + } + + usb_isochronous_data *isochronousData + = transfer->transfer->IsochronousData(); + + uint32 frameCount = OHCI_ITD_GET_FRAME_COUNT(descriptor->flags); + for (size_t i = 0; i < frameCount; i++, packet++) { + usb_iso_packet_descriptor* packet_descriptor + = &isochronousData->packet_descriptors[packet]; + + uint16 offset = descriptor->offset[OHCI_ITD_OFFSET_IDX(i)]; + uint8 code = OHCI_ITD_GET_BUFFER_CONDITION_CODE(offset); + packet_descriptor->status = _GetStatusOfConditionCode(code); + + // not touched by HC - sheduled too late to be processed + // in the requested frame - so we ignore it too + if (packet_descriptor->status == B_DEV_TOO_LATE) + continue; + + size_t len = OHCI_ITD_GET_BUFFER_LENGTH(offset); + if (!transfer->incoming) + len = packet_descriptor->request_length - len; + + packet_descriptor->actual_length = len; + actualLength += len; + } + + uint16 frame = OHCI_ITD_GET_STARTING_FRAME(descriptor->flags); + _ReleaseIsochronousBandwidth(frame, + OHCI_ITD_GET_FRAME_COUNT(descriptor->flags)); + + TRACE("ITD %p done\n", descriptor); + + if (descriptor == (ohci_isochronous_td*)transfer->last_descriptor) + break; + + descriptor + = (ohci_isochronous_td *)descriptor->next_done_descriptor; + } + } + + // remove the transfer from the list first so we are sure + // it doesn't get canceled while we still process it + if (Lock()) { + if (*_lastTransfer) + (*_lastTransfer)->link = transfer->link; + + if (transfer == fFirstTransfer) + fFirstTransfer = transfer->link; + if (transfer == fLastTransfer) + fLastTransfer = *_lastTransfer; + + // store the currently processing pipe here so we can wait + // in cancel if we are processing something on the target pipe + if (!transfer->canceled) + fProcessingPipe = transfer->transfer->TransferPipe(); + + transfer->link = NULL; + Unlock(); + } + + // break the descriptor chain on the last descriptor + transfer->last_descriptor->next_logical_descriptor = NULL; + TRACE("iso.transfer %p done with status 0x%08" B_PRIx32 " len:%ld\n", + transfer, callbackStatus, actualLength); + + // if canceled the callback has already been called + if (!transfer->canceled) { + if (callbackStatus == B_OK && actualLength > 0) { + if (transfer->data_descriptor && transfer->incoming) { + // data to read out + iovec *vector = transfer->transfer->Vector(); + size_t vectorCount = transfer->transfer->VectorCount(); + + transfer->transfer->PrepareKernelAccess(); + _ReadIsochronousDescriptorChain( + (ohci_isochronous_td*)transfer->data_descriptor, + vector, vectorCount); + } + } + + transfer->transfer->Finished(callbackStatus, actualLength); + fProcessingPipe = NULL; + } + + _FreeIsochronousDescriptorChain( + (ohci_isochronous_td*)transfer->first_descriptor); + + return true; +} + + status_t OHCI::_SubmitRequest(Transfer *transfer) { @@ -1307,8 +1480,58 @@ OHCI::_SubmitTransfer(Transfer *transfer) status_t OHCI::_SubmitIsochronousTransfer(Transfer *transfer) { - TRACE_ERROR("isochronous transfers not implemented\n"); - return B_ERROR; + Pipe *pipe = transfer->TransferPipe(); + bool directionIn = (pipe->Direction() == Pipe::In); + usb_isochronous_data *isochronousData = transfer->IsochronousData(); + + ohci_isochronous_td *firstDescriptor = NULL; + ohci_isochronous_td *lastDescriptor = NULL; + status_t result = _CreateIsochronousDescriptorChain(&firstDescriptor, + &lastDescriptor, transfer); + + if (firstDescriptor == 0 || lastDescriptor == 0) + return B_ERROR; + + if (result < B_OK) + return result; + + // Set the last descriptor to generate an interrupt + lastDescriptor->flags &= ~OHCI_ITD_INTERRUPT_MASK; + // let the controller retire last ITD + lastDescriptor->flags |= OHCI_ITD_SET_DELAY_INTERRUPT(1); + + // If direction is out set every descriptor data + if (pipe->Direction() == Pipe::Out) + _WriteIsochronousDescriptorChain(firstDescriptor, + transfer->Vector(), transfer->VectorCount()); + else + // Initialize the packet descriptors + for (uint32 i = 0; i < isochronousData->packet_count; i++) { + isochronousData->packet_descriptors[i].actual_length = 0; + isochronousData->packet_descriptors[i].status = B_NO_INIT; + } + + // Add to the transfer list + ohci_endpoint_descriptor *endpoint + = (ohci_endpoint_descriptor *)pipe->ControllerCookie(); + + MutexLocker endpointLocker(endpoint->lock); + result = _AddPendingIsochronousTransfer(transfer, endpoint, + firstDescriptor, lastDescriptor, directionIn); + if (result < B_OK) { + TRACE_ERROR("failed to add pending iso.transfer:" + "0x%08" B_PRIx32 "\n", result); + _FreeIsochronousDescriptorChain(firstDescriptor); + return result; + } + + // Add the descriptor chain to the endpoint + _SwitchIsochronousEndpointTail(endpoint, firstDescriptor, lastDescriptor); + endpointLocker.Unlock(); + + endpoint->flags &= ~OHCI_ENDPOINT_SKIP; + + return B_OK; } @@ -1352,6 +1575,54 @@ OHCI::_SwitchEndpointTail(ohci_endpoint_descriptor *endpoint, } +void +OHCI::_SwitchIsochronousEndpointTail(ohci_endpoint_descriptor *endpoint, + ohci_isochronous_td *first, ohci_isochronous_td *last) +{ + // fill in the information of the first descriptor into the current tail + ohci_isochronous_td *tail + = (ohci_isochronous_td*)endpoint->tail_logical_descriptor; + tail->flags = first->flags; + tail->buffer_page_byte_0 = first->buffer_page_byte_0; + tail->next_physical_descriptor = first->next_physical_descriptor; + tail->last_byte_address = first->last_byte_address; + tail->buffer_size = first->buffer_size; + tail->buffer_logical = first->buffer_logical; + tail->next_logical_descriptor = first->next_logical_descriptor; + tail->next_done_descriptor = first->next_done_descriptor; + + // the first descriptor becomes the new tail + first->flags = 0; + first->buffer_page_byte_0 = 0; + first->next_physical_descriptor = 0; + first->last_byte_address = 0; + first->buffer_size = 0; + first->buffer_logical = NULL; + first->next_logical_descriptor = NULL; + first->next_done_descriptor = NULL; + + for (int i = 0; i < OHCI_ITD_NOFFSET; i++) { + tail->offset[i] = first->offset[i]; + first->offset[i] = 0; + } + + if (first == last) + _LinkIsochronousDescriptors(tail, first, NULL); + else + _LinkIsochronousDescriptors(last, first, NULL); + + // update the endpoint tail pointer to reflect the change + endpoint->tail_logical_descriptor = first; + endpoint->tail_physical_descriptor = (uint32)first->physical_address; + TRACE("switched tail from %p to %p\n", tail, first); + +#if 0 + _PrintEndpoint(endpoint); + _PrintDescriptorChain(tail); +#endif +} + + void OHCI::_RemoveTransferFromEndpoint(transfer_data *transfer) { @@ -1515,9 +1786,11 @@ OHCI::_InsertEndpointForPipe(Pipe *pipe) if (pipe->Type() & USB_OBJECT_ISO_PIPE) { // Set the isochronous bit format endpoint->flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT; - // TODO - _FreeEndpoint(endpoint); - return B_ERROR; + ohci_isochronous_td *tail = _CreateIsochronousDescriptor(0); + tail->flags = 0; + endpoint->tail_logical_descriptor = tail; + endpoint->head_physical_descriptor = tail->physical_address; + endpoint->tail_physical_descriptor = tail->physical_address; } else { ohci_general_td *tail = _CreateGeneralDescriptor(0); tail->flags = 0; @@ -1691,6 +1964,191 @@ OHCI::_FreeDescriptorChain(ohci_general_td *topDescriptor) } +ohci_isochronous_td * +OHCI::_CreateIsochronousDescriptor(size_t bufferSize) +{ + ohci_isochronous_td *descriptor = NULL; + phys_addr_t physicalAddress; + + if (fStack->AllocateChunk((void **)&descriptor, &physicalAddress, + sizeof(ohci_isochronous_td)) != B_OK) { + TRACE_ERROR("failed to allocate isochronous descriptor\n"); + return NULL; + } + + descriptor->physical_address = (uint32)physicalAddress; + descriptor->next_physical_descriptor = 0; + descriptor->next_logical_descriptor = NULL; + descriptor->next_done_descriptor = NULL; + descriptor->buffer_size = bufferSize; + if (bufferSize == 0) { + descriptor->buffer_page_byte_0 = 0; + descriptor->buffer_logical = NULL; + descriptor->last_byte_address = 0; + return descriptor; + } + + if (fStack->AllocateChunk(&descriptor->buffer_logical, + &physicalAddress, bufferSize) != B_OK) { + TRACE_ERROR("failed to allocate space for iso.buffer\n"); + fStack->FreeChunk(descriptor, descriptor->physical_address, + sizeof(ohci_isochronous_td)); + return NULL; + } + descriptor->buffer_page_byte_0 = (uint32)physicalAddress; + descriptor->last_byte_address + = descriptor->buffer_page_byte_0 + bufferSize - 1; + + return descriptor; +} + + +void +OHCI::_FreeIsochronousDescriptor(ohci_isochronous_td *descriptor) +{ + if (!descriptor) + return; + + if (descriptor->buffer_logical) { + fStack->FreeChunk(descriptor->buffer_logical, + descriptor->buffer_page_byte_0, descriptor->buffer_size); + } + + fStack->FreeChunk((void *)descriptor, descriptor->physical_address, + sizeof(ohci_general_td)); +} + + +status_t +OHCI::_CreateIsochronousDescriptorChain(ohci_isochronous_td **_firstDescriptor, + ohci_isochronous_td **_lastDescriptor, Transfer *transfer) +{ + Pipe *pipe = transfer->TransferPipe(); + usb_isochronous_data *isochronousData = transfer->IsochronousData(); + + size_t dataLength = transfer->VectorLength(); + size_t packet_count = isochronousData->packet_count; + + if (packet_count == 0) { + TRACE_ERROR("isochronous packet_count should not be equal to zero."); + return B_BAD_VALUE; + } + + size_t packetSize = dataLength / packet_count; + if (dataLength % packet_count != 0) + packetSize++; + + if (packetSize > pipe->MaxPacketSize()) { + TRACE_ERROR("isochronous packetSize %ld is bigger" + " than pipe MaxPacketSize %ld.", packetSize, pipe->MaxPacketSize()); + return B_BAD_VALUE; + } + + uint16 bandwidth = transfer->Bandwidth() / packet_count; + if (transfer->Bandwidth() % packet_count != 0) + bandwidth++; + + ohci_isochronous_td *firstDescriptor = NULL; + ohci_isochronous_td *lastDescriptor = *_firstDescriptor; + + // the frame number currently processed by the host controller + uint16 currentFrame = fHcca->current_frame_number & 0xFFFF; + uint16 safeFrames = 5; + + // The entry where to start inserting the first Isochronous descriptor + // real frame number may differ in case provided one has not bandwidth + if (isochronousData->flags & USB_ISO_ASAP || + isochronousData->starting_frame_number == NULL) + // We should stay about 5-10 ms ahead of the controller + // USB1 frame is equal to 1 ms + currentFrame += safeFrames; + else + currentFrame = *isochronousData->starting_frame_number; + + uint16 packets = packet_count; + uint16 frameOffset = 0; + while (packets > 0) { + // look for up to 8 continous frames with available bandwidth + uint16 frameCount = 0; + while (frameCount < min_c(OHCI_ITD_NOFFSET, packets) + && _AllocateIsochronousBandwidth(frameOffset + currentFrame + + frameCount, bandwidth)) + frameCount++; + + if (frameCount == 0) { + // starting frame has no bandwidth for our transaction - try next + if (++frameOffset >= 0xFFFF) { + TRACE_ERROR("failed to allocate bandwidth\n"); + _FreeIsochronousDescriptorChain(firstDescriptor); + return B_NO_MEMORY; + } + continue; + } + + ohci_isochronous_td *descriptor = _CreateIsochronousDescriptor( + packetSize * frameCount); + + if (!descriptor) { + TRACE_ERROR("failed to allocate ITD\n"); + _ReleaseIsochronousBandwidth(currentFrame + frameOffset, frameCount); + _FreeIsochronousDescriptorChain(firstDescriptor); + return B_NO_MEMORY; + } + + uint16 pageOffset = descriptor->buffer_page_byte_0 & 0xfff; + descriptor->buffer_page_byte_0 &= ~0xfff; + for (uint16 i = 0; i < frameCount; i++) { + descriptor->offset[OHCI_ITD_OFFSET_IDX(i)] + = OHCI_ITD_MK_OFFS(pageOffset + packetSize * i); + } + + descriptor->flags = OHCI_ITD_SET_FRAME_COUNT(frameCount) + | OHCI_ITD_SET_CONDITION_CODE(OHCI_ITD_CONDITION_NOT_ACCESSED) + | OHCI_ITD_SET_DELAY_INTERRUPT(OHCI_ITD_INTERRUPT_NONE) + | OHCI_ITD_SET_STARTING_FRAME(currentFrame + frameOffset); + + // the last packet may be shorter than other ones in this transfer + if (packets <= OHCI_ITD_NOFFSET) + descriptor->last_byte_address + += dataLength - packetSize * (packet_count); + + // link to previous + if (lastDescriptor) + _LinkIsochronousDescriptors(lastDescriptor, descriptor, descriptor); + + lastDescriptor = descriptor; + if (!firstDescriptor) + firstDescriptor = descriptor; + + packets -= frameCount; + + frameOffset += frameCount; + + if (packets == 0 && isochronousData->starting_frame_number) + *isochronousData->starting_frame_number = currentFrame + frameOffset; + } + + *_firstDescriptor = firstDescriptor; + *_lastDescriptor = lastDescriptor; + + return B_OK; +} + + +void +OHCI::_FreeIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor) +{ + ohci_isochronous_td *current = topDescriptor; + ohci_isochronous_td *next = NULL; + + while (current) { + next = (ohci_isochronous_td *)current->next_done_descriptor; + _FreeIsochronousDescriptor(current); + current = next; + } +} + + size_t OHCI::_WriteDescriptorChain(ohci_general_td *topDescriptor, iovec *vector, size_t vectorCount) @@ -1746,6 +2204,61 @@ OHCI::_WriteDescriptorChain(ohci_general_td *topDescriptor, iovec *vector, } +size_t +OHCI::_WriteIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor, + iovec *vector, size_t vectorCount) +{ + ohci_isochronous_td *current = topDescriptor; + size_t actualLength = 0; + size_t vectorIndex = 0; + size_t vectorOffset = 0; + size_t bufferOffset = 0; + + while (current) { + if (!current->buffer_logical) + break; + + while (true) { + size_t length = min_c(current->buffer_size - bufferOffset, + vector[vectorIndex].iov_len - vectorOffset); + + TRACE("copying %ld bytes to bufferOffset %ld from" + " vectorOffset %ld at index %ld of %ld\n", length, bufferOffset, + vectorOffset, vectorIndex, vectorCount); + memcpy((uint8 *)current->buffer_logical + bufferOffset, + (uint8 *)vector[vectorIndex].iov_base + vectorOffset, length); + + actualLength += length; + vectorOffset += length; + bufferOffset += length; + + if (vectorOffset >= vector[vectorIndex].iov_len) { + if (++vectorIndex >= vectorCount) { + TRACE("wrote descriptor chain (%ld bytes, no" + " more vectors)\n", actualLength); + return actualLength; + } + + vectorOffset = 0; + } + + if (bufferOffset >= current->buffer_size) { + bufferOffset = 0; + break; + } + } + + if (!current->next_logical_descriptor) + break; + + current = (ohci_isochronous_td *)current->next_logical_descriptor; + } + + TRACE("wrote descriptor chain (%ld bytes)\n", actualLength); + return actualLength; +} + + size_t OHCI::_ReadDescriptorChain(ohci_general_td *topDescriptor, iovec *vector, size_t vectorCount) @@ -1805,6 +2318,59 @@ OHCI::_ReadDescriptorChain(ohci_general_td *topDescriptor, iovec *vector, } +void +OHCI::_ReadIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor, + iovec *vector, size_t vectorCount) +{ + ohci_isochronous_td *current = topDescriptor; + size_t actualLength = 0; + size_t vectorIndex = 0; + size_t vectorOffset = 0; + size_t bufferOffset = 0; + + while (current && OHCI_ITD_GET_CONDITION_CODE(current->flags) + != OHCI_ITD_CONDITION_NOT_ACCESSED) { + size_t bufferSize = current->buffer_size; + if (current->buffer_logical != NULL && bufferSize > 0) { + while (true) { + size_t length = min_c(bufferSize - bufferOffset, + vector[vectorIndex].iov_len - vectorOffset); + + TRACE("copying %ld bytes to vectorOffset %ld from bufferOffset" + " %ld at index %ld of %ld\n", length, vectorOffset, + bufferOffset, vectorIndex, vectorCount); + memcpy((uint8 *)vector[vectorIndex].iov_base + vectorOffset, + (uint8 *)current->buffer_logical + bufferOffset, length); + + actualLength += length; + vectorOffset += length; + bufferOffset += length; + + if (vectorOffset >= vector[vectorIndex].iov_len) { + if (++vectorIndex >= vectorCount) { + TRACE("read descriptor chain (%ld bytes, " + "no more vectors)\n", actualLength); + return; + } + + vectorOffset = 0; + } + + if (bufferOffset >= bufferSize) { + bufferOffset = 0; + break; + } + } + } + + current = (ohci_isochronous_td *)current->next_done_descriptor; + } + + TRACE("read descriptor chain (%ld bytes)\n", actualLength); + return; +} + + size_t OHCI::_ReadActualLength(ohci_general_td *topDescriptor) { @@ -1836,18 +2402,85 @@ OHCI::_LinkDescriptors(ohci_general_td *first, ohci_general_td *second) } -ohci_isochronous_td * -OHCI::_CreateIsochronousDescriptor() +void +OHCI::_LinkIsochronousDescriptors(ohci_isochronous_td *first, + ohci_isochronous_td *second, ohci_isochronous_td *nextDone) { - // TODO - return NULL; + first->next_physical_descriptor = second->physical_address; + first->next_logical_descriptor = second; + first->next_done_descriptor = nextDone; +} + + +bool +OHCI::_AllocateIsochronousBandwidth(uint16 frame, uint16 size) +{ + frame %= NUMBER_OF_FRAMES; + if (size > fFrameBandwidth[frame]) + return false; + + fFrameBandwidth[frame]-= size; + return true; } void -OHCI::_FreeIsochronousDescriptor(ohci_isochronous_td *descriptor) +OHCI::_ReleaseIsochronousBandwidth(uint16 startFrame, uint16 frameCount) { - // TODO + for (size_t index = 0; index < frameCount; index++) { + uint16 frame = (startFrame + index) % NUMBER_OF_FRAMES; + fFrameBandwidth[frame] = MAX_AVAILABLE_BANDWIDTH; + } +} + + +status_t +OHCI::_GetStatusOfConditionCode(uint8 conditionCode) +{ + switch (conditionCode) { + case OHCI_TD_CONDITION_NO_ERROR: + return B_OK; + + case OHCI_TD_CONDITION_CRC_ERROR: + case OHCI_TD_CONDITION_BIT_STUFFING: + case OHCI_TD_CONDITION_TOGGLE_MISMATCH: + return B_DEV_CRC_ERROR; + + case OHCI_TD_CONDITION_STALL: + return B_DEV_STALLED; + + case OHCI_TD_CONDITION_NO_RESPONSE: + return B_TIMED_OUT; + + case OHCI_TD_CONDITION_PID_CHECK_FAILURE: + return B_DEV_BAD_PID; + + case OHCI_TD_CONDITION_UNEXPECTED_PID: + return B_DEV_UNEXPECTED_PID; + + case OHCI_TD_CONDITION_DATA_OVERRUN: + return B_DEV_DATA_OVERRUN; + + case OHCI_TD_CONDITION_DATA_UNDERRUN: + return B_DEV_DATA_UNDERRUN; + + case OHCI_TD_CONDITION_BUFFER_OVERRUN: + return B_DEV_FIFO_OVERRUN; + + case OHCI_TD_CONDITION_BUFFER_UNDERRUN: + return B_DEV_FIFO_UNDERRUN; + + case OHCI_TD_CONDITION_NOT_ACCESSED: + return B_DEV_PENDING; + + case 0x0E: + return B_DEV_TOO_LATE; // PSW: _NOT_ACCESSED + + default: + break; + } + + return B_ERROR; } @@ -1882,7 +2515,7 @@ OHCI::_ReadReg(uint32 reg) void OHCI::_PrintEndpoint(ohci_endpoint_descriptor *endpoint) { - TRACE_ALWAYS("endpoint %p\n", endpoint); + dprintf("endpoint %p\n", endpoint); dprintf("\tflags........... 0x%08" B_PRIx32 "\n", endpoint->flags); dprintf("\ttail_physical... 0x%08" B_PRIx32 "\n", endpoint->tail_physical_descriptor); dprintf("\thead_physical... 0x%08" B_PRIx32 "\n", endpoint->head_physical_descriptor); @@ -1897,7 +2530,7 @@ void OHCI::_PrintDescriptorChain(ohci_general_td *topDescriptor) { while (topDescriptor) { - TRACE_ALWAYS("descriptor %p\n", topDescriptor); + dprintf("descriptor %p\n", topDescriptor); dprintf("\tflags........... 0x%08" B_PRIx32 "\n", topDescriptor->flags); dprintf("\tbuffer_physical. 0x%08" B_PRIx32 "\n", topDescriptor->buffer_physical); dprintf("\tnext_physical... 0x%08" B_PRIx32 "\n", topDescriptor->next_physical_descriptor); @@ -1910,3 +2543,30 @@ OHCI::_PrintDescriptorChain(ohci_general_td *topDescriptor) topDescriptor = (ohci_general_td *)topDescriptor->next_logical_descriptor; } } + + +void +OHCI::_PrintDescriptorChain(ohci_isochronous_td *topDescriptor) +{ + while (topDescriptor) { + dprintf("iso.descriptor %p\n", topDescriptor); + dprintf("\tflags........... 0x%08" B_PRIx32 "\n", topDescriptor->flags); + dprintf("\tbuffer_pagebyte0 0x%08" B_PRIx32 "\n", topDescriptor->buffer_page_byte_0); + dprintf("\tnext_physical... 0x%08" B_PRIx32 "\n", topDescriptor->next_physical_descriptor); + dprintf("\tlast_byte....... 0x%08" B_PRIx32 "\n", topDescriptor->last_byte_address); + dprintf("\toffset:\n\t0x%04x 0x%04x 0x%04x 0x%04x\n" + "\t0x%04x 0x%04x 0x%04x 0x%04x\n", + topDescriptor->offset[0], topDescriptor->offset[1], + topDescriptor->offset[2], topDescriptor->offset[3], + topDescriptor->offset[4], topDescriptor->offset[5], + topDescriptor->offset[6], topDescriptor->offset[7]); + dprintf("\tphysical........ 0x%08" B_PRIx32 "\n", topDescriptor->physical_address); + dprintf("\tbuffer_size..... %lu\n", topDescriptor->buffer_size); + dprintf("\tbuffer_logical.. %p\n", topDescriptor->buffer_logical); + dprintf("\tnext_logical.... %p\n", topDescriptor->next_logical_descriptor); + dprintf("\tnext_done....... %p\n", topDescriptor->next_done_descriptor); + + topDescriptor = (ohci_isochronous_td *)topDescriptor->next_done_descriptor; + } +} + diff --git a/src/add-ons/kernel/busses/usb/ohci.h b/src/add-ons/kernel/busses/usb/ohci.h index 341e8a46fd..73276530d7 100644 --- a/src/add-ons/kernel/busses/usb/ohci.h +++ b/src/add-ons/kernel/busses/usb/ohci.h @@ -1,11 +1,12 @@ /* - * Copyright 2005-2008, Haiku Inc. All rights reserved. + * Copyright 2005-2013, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Jan-Rixt Van Hoye * Salvatore Benedetto * Michael Lotz + * Siarzhuk Zharski */ #ifndef OHCI_H #define OHCI_H @@ -69,12 +70,20 @@ static int32 _InterruptHandler(void *data); ohci_general_td *dataDescriptor, ohci_general_td *lastDescriptor, bool directionIn); - status_t _CancelQueuedIsochronousTransfers( - Pipe *pipe, bool force); + status_t _AddPendingIsochronousTransfer( + Transfer *transfer, + ohci_endpoint_descriptor *endpoint, + ohci_isochronous_td *firstDescriptor, + ohci_isochronous_td *lastDescriptor, + bool directionIn); + status_t _UnlinkTransfer(transfer_data *transfer); static int32 _FinishThread(void *data); void _FinishTransfers(); + bool _FinishIsochronousTransfer( + transfer_data *transfer, + transfer_data **_lastTransfer); status_t _SubmitRequest(Transfer *transfer); status_t _SubmitTransfer(Transfer *transfer); @@ -85,6 +94,11 @@ static int32 _FinishThread(void *data); ohci_endpoint_descriptor *endpoint, ohci_general_td *first, ohci_general_td *last); + void _SwitchIsochronousEndpointTail( + ohci_endpoint_descriptor *endpoint, + ohci_isochronous_td *first, + ohci_isochronous_td *last); + void _RemoveTransferFromEndpoint( transfer_data *transfer); @@ -101,7 +115,6 @@ static int32 _FinishThread(void *data); size_t bufferSize); void _FreeGeneralDescriptor( ohci_general_td *descriptor); - status_t _CreateDescriptorChain( ohci_general_td **firstDescriptor, ohci_general_td **lastDescriptor, @@ -110,22 +123,48 @@ static int32 _FinishThread(void *data); void _FreeDescriptorChain( ohci_general_td *topDescriptor); + ohci_isochronous_td * _CreateIsochronousDescriptor( + size_t bufferSize); + void _FreeIsochronousDescriptor( + ohci_isochronous_td *descriptor); + status_t _CreateIsochronousDescriptorChain( + ohci_isochronous_td **firstDescriptor, + ohci_isochronous_td **lastDescriptor, + Transfer *transfer); + void _FreeIsochronousDescriptorChain( + ohci_isochronous_td *topDescriptor); + size_t _WriteDescriptorChain( ohci_general_td *topDescriptor, iovec *vector, size_t vectorCount); size_t _ReadDescriptorChain( ohci_general_td *topDescriptor, iovec *vector, size_t vectorCount); + + size_t _WriteIsochronousDescriptorChain( + ohci_isochronous_td *topDescriptor, + iovec *vector, size_t vectorCount); + void _ReadIsochronousDescriptorChain( + ohci_isochronous_td *topDescriptor, + iovec *vector, size_t vectorCount); + size_t _ReadActualLength( ohci_general_td *topDescriptor); void _LinkDescriptors(ohci_general_td *first, ohci_general_td *second); + void _LinkIsochronousDescriptors( + ohci_isochronous_td *first, + ohci_isochronous_td *second, + ohci_isochronous_td *nextDone); - ohci_isochronous_td * _CreateIsochronousDescriptor(); - void _FreeIsochronousDescriptor( - ohci_isochronous_td *descriptor); + bool _AllocateIsochronousBandwidth(uint16 frame, + uint16 size); + void _ReleaseIsochronousBandwidth( + uint16 startFrame, uint16 count); + status_t _GetStatusOfConditionCode( + uint8 conditionCode); // Private locking bool _LockEndpoints(); void _UnlockEndpoints(); @@ -139,6 +178,8 @@ inline uint32 _ReadReg(uint32 reg); ohci_endpoint_descriptor *endpoint); void _PrintDescriptorChain( ohci_general_td *topDescriptor); + void _PrintDescriptorChain( + ohci_isochronous_td *topDescriptor); static pci_module_info * sPCIModule; static pci_x86_module_info * sPCIx86Module; @@ -167,6 +208,8 @@ static pci_x86_module_info * sPCIx86Module; thread_id fFinishThread; bool fStopFinishThread; Pipe * fProcessingPipe; + // frame bandwidth watchdogs array + uint16 * fFrameBandwidth; // Root Hub OHCIRootHub * fRootHub; diff --git a/src/add-ons/kernel/busses/usb/ohci.rdef b/src/add-ons/kernel/busses/usb/ohci.rdef index a70dc36f01..be184dde7a 100644 --- a/src/add-ons/kernel/busses/usb/ohci.rdef +++ b/src/add-ons/kernel/busses/usb/ohci.rdef @@ -12,6 +12,6 @@ resource app_version { variety = 0, internal = 0, short_info = "OHCI host controller driver", - long_info = "Haiku OHCI HCD - Copyright 2005-2009, Haiku Inc." + long_info = "Haiku OHCI HCD - Copyright 2005-2013, Haiku Inc." }; diff --git a/src/add-ons/kernel/busses/usb/ohci_hardware.h b/src/add-ons/kernel/busses/usb/ohci_hardware.h index 308a27b182..4244796c9e 100644 --- a/src/add-ons/kernel/busses/usb/ohci_hardware.h +++ b/src/add-ons/kernel/busses/usb/ohci_hardware.h @@ -1,11 +1,12 @@ /* - * Copyright 2005-2008, Haiku Inc. All rights reserved. + * Copyright 2005-2013, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Jan-Rixt Van Hoye * Salvatore Benedetto * Michael Lotz + * Siarzhuk Zharski */ #ifndef OHCI_HARDWARE_H @@ -396,6 +397,8 @@ typedef struct { uint16 offset[OHCI_ITD_NOFFSET]; // Buffer offsets // Software part uint32 physical_address; // Physical address of this descriptor + size_t buffer_size; // Size of the buffer + void *buffer_logical; // Logical pointer to the buffer void *next_logical_descriptor; // Logical pointer next descriptor void *next_done_descriptor; // Used for collision in the hash table } ohci_isochronous_td; @@ -404,11 +407,31 @@ typedef struct { #define OHCI_ITD_SET_STARTING_FRAME(x) ((x) & 0xffff) #define OHCI_ITD_GET_DELAY_INTERRUPT(x) (((x) >> 21) & 7) #define OHCI_ITD_SET_DELAY_INTERRUPT(x) ((x) << 21) -#define OHCI_ITD_NO_INTERRUPT 0x00e00000 +#define OHCI_ITD_INTERRUPT_MASK 0x00e00000 #define OHCI_ITD_GET_FRAME_COUNT(x) ((((x) >> 24) & 7) + 1) #define OHCI_ITD_SET_FRAME_COUNT(x) (((x) - 1) << 24) #define OHCI_ITD_GET_CONDITION_CODE(x) ((x) >> 28) -#define OHCI_ITD_NO_CONDITION_CODE 0xf0000000 +#define OHCI_ITD_SET_CONDITION_CODE(x) ((x) << 28) +#define OHCI_ITD_CONDITION_CODE_MASK 0xf0000000 + +#define OHCI_ITD_OFFSET_IDX(x) (x) + +#define OHCI_ITD_INTERRUPT_IMMEDIATE 0x00 +#define OHCI_ITD_INTERRUPT_NONE 0x07 + +#define OHCI_ITD_CONDITION_NO_ERROR 0x00 +#define OHCI_ITD_CONDITION_CRC_ERROR 0x01 +#define OHCI_ITD_CONDITION_BIT_STUFFING 0x02 +#define OHCI_ITD_CONDITION_TOGGLE_MISMATCH 0x03 +#define OHCI_ITD_CONDITION_STALL 0x04 +#define OHCI_ITD_CONDITION_NO_RESPONSE 0x05 +#define OHCI_ITD_CONDITION_PID_CHECK_FAILURE 0x06 +#define OHCI_ITD_CONDITION_UNEXPECTED_PID 0x07 +#define OHCI_ITD_CONDITION_DATA_OVERRUN 0x08 +#define OHCI_ITD_CONDITION_DATA_UNDERRUN 0x09 +#define OHCI_ITD_CONDITION_BUFFER_OVERRUN 0x0c +#define OHCI_ITD_CONDITION_BUFFER_UNDERRUN 0x0d +#define OHCI_ITD_CONDITION_NOT_ACCESSED 0x0f // TO FIX #define itd_pswn itd_offset // Packet Status Word @@ -419,6 +442,10 @@ typedef struct { #define OHCI_ISOCHRONOUS_TD_ALIGN 32 +// Number of frames +#define NUMBER_OF_FRAMES 1024 +#define MAX_AVAILABLE_BANDWIDTH 900 // Microseconds + // -------------------------------- // Completion Codes (section 4.3.3) // --------------------------------