/* * Copyright 2003-2006, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz * Niels S. Reedijk */ #include "usb_private.h" #include Transfer::Transfer(Pipe *pipe) : fPipe(pipe), fVector(&fData), fVectorCount(0), fBaseAddress(NULL), fPhysical(false), fFragmented(false), fActualLength(0), fUserArea(-1), fClonedArea(-1), fCallback(NULL), fCallbackCookie(NULL), fRequestData(NULL), fIsochronousData(NULL), fBandwidth(0) { } Transfer::~Transfer() { // we take ownership of the request data if (fRequestData) delete fRequestData; if (fVector && fVector != &fData) delete[] fVector; if (fClonedArea >= B_OK) delete_area(fClonedArea); } void Transfer::SetRequestData(usb_request_data *data) { fRequestData = data; } void Transfer::SetIsochronousData(usb_isochronous_data *data) { fIsochronousData = data; } void Transfer::SetData(uint8 *data, size_t dataLength) { fPhysical = false; fBaseAddress = data; fData.base = (generic_addr_t)data; fData.length = dataLength; if (data && dataLength > 0) fVectorCount = 1; fFragmented = dataLength > USB_MAX_FRAGMENT_SIZE; // Calculate the bandwidth (only if it is not a bulk transfer) if (!(fPipe->Type() & USB_OBJECT_BULK_PIPE)) { if (_CalculateBandwidth() < B_OK) TRACE_ERROR("can't calculate bandwidth\n"); } } void Transfer::SetVector(iovec *vector, size_t vectorCount) { fPhysical = false; fVector = new(std::nothrow) generic_io_vec[vectorCount]; for (size_t i = 0; i < vectorCount; i++) { fVector[i].base = (generic_addr_t)vector[i].iov_base; fVector[i].length = vector[i].iov_len; } fVectorCount = vectorCount; fBaseAddress = vector[0].iov_base; _CheckFragmented(); } void Transfer::SetVector(physical_entry *vector, size_t vectorCount) { fPhysical = true; fVector = new(std::nothrow) generic_io_vec[vectorCount]; for (size_t i = 0; i < vectorCount; i++) { fVector[i].base = (generic_addr_t)vector[i].address; fVector[i].length = vector[i].size; } fVectorCount = vectorCount; fBaseAddress = NULL; _CheckFragmented(); } void Transfer::_CheckFragmented() { size_t length = 0; for (size_t i = 0; i < fVectorCount && length <= USB_MAX_FRAGMENT_SIZE; i++) length += fVector[i].length; fFragmented = length > USB_MAX_FRAGMENT_SIZE; } size_t Transfer::FragmentLength() const { size_t length = 0; for (size_t i = 0; i < fVectorCount; i++) length += fVector[i].length; if (length > USB_MAX_FRAGMENT_SIZE) length = USB_MAX_FRAGMENT_SIZE; return length; } void Transfer::AdvanceByFragment(size_t actualLength) { size_t length = USB_MAX_FRAGMENT_SIZE; for (size_t i = 0; i < fVectorCount; i++) { if (fVector[i].length <= length) { length -= fVector[i].length; fVector[i].length = 0; continue; } fVector[i].base = fVector[i].base + length; fVector[i].length -= length; break; } fActualLength += actualLength; } status_t Transfer::InitKernelAccess() { // nothing to do if we are already prepared, or have a physical request if (fClonedArea >= B_OK || fPhysical) return B_OK; // we might need to access a buffer in userspace. this will not // be possible in the kernel space finisher thread unless we // get the proper area id for the space we need and then clone it // before reading from or writing to it. generic_io_vec *vector = fVector; for (size_t i = 0; i < fVectorCount; i++) { if (IS_USER_ADDRESS(vector[i].base)) { fUserArea = area_for((void*)vector[i].base); if (fUserArea < B_OK) { TRACE_ERROR("failed to find area for user space buffer!\n"); return B_BAD_ADDRESS; } break; } } // no user area required for access if (fUserArea < B_OK) return B_OK; area_info areaInfo; if (fUserArea < B_OK || get_area_info(fUserArea, &areaInfo) < B_OK) { TRACE_ERROR("couldn't get user area info\n"); return B_BAD_ADDRESS; } for (size_t i = 0; i < fVectorCount; i++) { vector[i].base = vector[i].base - (addr_t)areaInfo.address; if (vector[i].base > areaInfo.size || (vector[i].base + vector[i].length) > areaInfo.size) { TRACE_ERROR("data buffer spans across multiple areas!\n"); return B_BAD_ADDRESS; } } return B_OK; } status_t Transfer::PrepareKernelAccess() { // done if there is no userspace buffer or if we already cloned its area if (fUserArea < B_OK || fClonedArea >= B_OK) return B_OK; void *clonedMemory = NULL; // we got a userspace buffer, need to clone the area for that // space first and map the iovecs to this cloned area. fClonedArea = clone_area("userspace accessor", &clonedMemory, B_ANY_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, fUserArea); if (fClonedArea < B_OK) return fClonedArea; for (size_t i = 0; i < fVectorCount; i++) fVector[i].base = fVector[i].base + (addr_t)clonedMemory; return B_OK; } void Transfer::SetCallback(usb_callback_func callback, void *cookie) { fCallback = callback; fCallbackCookie = cookie; } void Transfer::Finished(uint32 status, size_t actualLength) { if (fCallback) fCallback(fCallbackCookie, status, fBaseAddress, fActualLength + actualLength); } /* * USB 2.0 Spec function, pag 64. * This function sets fBandwidth in microsecond * to the bandwidth needed to transfer fData.iov_len bytes. * The calculation is based on * 1. Speed of the transfer * 2. Pipe direction * 3. Type of pipe * 4. Number of bytes to transfer */ status_t Transfer::_CalculateBandwidth() { uint16 bandwidthNS; uint32 type = fPipe->Type(); switch (fPipe->Speed()) { case USB_SPEED_HIGHSPEED: { // Direction doesn't matter for highspeed if (type & USB_OBJECT_ISO_PIPE) bandwidthNS = (uint16)((38 * 8 * 2.083) + (2.083 * ((uint32)(3.167 * (1.1667 * 8 * fData.length)))) + USB_BW_HOST_DELAY); else bandwidthNS = (uint16)((55 * 8 * 2.083) + (2.083 * ((uint32)(3.167 * (1.1667 * 8 * fData.length)))) + USB_BW_HOST_DELAY); break; } case USB_SPEED_FULLSPEED: { // Direction does matter this time for isochronous if (type & USB_OBJECT_ISO_PIPE) bandwidthNS = (uint16) (((fPipe->Direction() == Pipe::In) ? 7268 : 6265) + (83.54 * ((uint32)(3.167 + (1.1667 * 8 * fData.length)))) + USB_BW_HOST_DELAY); else bandwidthNS = (uint16)(9107 + (83.54 * ((uint32)(3.167 + (1.1667 * 8 * fData.length)))) + USB_BW_HOST_DELAY); break; } case USB_SPEED_LOWSPEED: { if (fPipe->Direction() == Pipe::In) bandwidthNS = (uint16) (64060 + (2 * USB_BW_SETUP_LOW_SPEED_PORT_DELAY) + (676.67 * ((uint32)(3.167 + (1.1667 * 8 * fData.length)))) + USB_BW_HOST_DELAY); else bandwidthNS = (uint16)(64107 + (2 * USB_BW_SETUP_LOW_SPEED_PORT_DELAY) + (667.0 * ((uint32)(3.167 + (1.1667 * 8 * fData.length)))) + USB_BW_HOST_DELAY); break; } case USB_SPEED_SUPERSPEED: { // TODO it should only be useful for isochronous type bandwidthNS = 0; break; } default: // We should never get here TRACE("speed unknown"); return B_ERROR; } // Round up and set the value in microseconds fBandwidth = (bandwidthNS + 500) / 1000; return B_OK; }