/*
 * Copyright 2003-2006, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Michael Lotz <mmlr@mlotz.ch>
 *		Niels S. Reedijk
 */
 
 
#include "usb_private.h"
 
#include <kernel.h>
 
 
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)
{
	fBaseAddress = data;
	fData.iov_base = data;
	fData.iov_len = dataLength;
 
	if (data && dataLength > 0)
		fVectorCount = 1;
 
	// 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::SetPhysical(bool physical)
{
	fPhysical = physical;
}
 
 
void
Transfer::SetVector(iovec *vector, size_t vectorCount)
{
	fVector = new(std::nothrow) iovec[vectorCount];
	memcpy(fVector, vector, vectorCount * sizeof(iovec));
	fVectorCount = vectorCount;
	fBaseAddress = fVector[0].iov_base;
}
 
 
size_t
Transfer::VectorLength()
{
	size_t length = 0;
	for (size_t i = 0; i < fVectorCount; i++)
		length += fVector[i].iov_len;
 
	// the data is to large and would overflow the allocator
	if (length > USB_MAX_FRAGMENT_SIZE) {
		length = USB_MAX_FRAGMENT_SIZE;
		fFragmented = true;
	}
 
	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].iov_len <= length) {
			length -= fVector[i].iov_len;
			fVector[i].iov_len = 0;
			continue;
		}
 
		fVector[i].iov_base = (uint8 *)fVector[i].iov_base + length;
		fVector[i].iov_len -= length;
		break;
	}
 
	fActualLength += actualLength;
}
 
 
status_t
Transfer::InitKernelAccess()
{
	// nothing to do if we are already prepared
	if (fClonedArea >= B_OK)
		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.
	iovec *vector = fVector;
	for (size_t i = 0; i < fVectorCount; i++) {
		if (IS_USER_ADDRESS(vector[i].iov_base)) {
			fUserArea = area_for(vector[i].iov_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].iov_base = (uint8 *)vector[i].iov_base - (addr_t)areaInfo.address;
 
		if ((size_t)vector[i].iov_base > areaInfo.size
			|| (size_t)vector[i].iov_base + vector[i].iov_len > 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].iov_base = (uint8 *)fVector[i].iov_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.iov_len))))
					+ USB_BW_HOST_DELAY);
			else
				bandwidthNS = (uint16)((55 * 8 * 2.083)
					+ (2.083 * ((uint32)(3.167 * (1.1667 * 8 * fData.iov_len))))
					+ 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.iov_len))))
					+ USB_BW_HOST_DELAY);
			else
				bandwidthNS = (uint16)(9107
					+ (83.54 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len))))
					+ 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.iov_len))))
					+ 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.iov_len))))
					+ USB_BW_HOST_DELAY);
			break;
		}
 
		case USB_SPEED_SUPER:
		{
			// 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;
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fData.