* 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()
{
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;
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()
{
if (fClonedArea >= B_OK || fPhysical)
return B_OK;
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;
}
}
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()
{
if (fUserArea < B_OK || fClonedArea >= B_OK)
return B_OK;
void *clonedMemory = NULL;
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:
{
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:
{
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:
case USB_SPEED_SUPERSPEEDPLUS:
{
bandwidthNS = 0;
break;
}
default:
TRACE("speed unknown");
return B_ERROR;
}
fBandwidth = (bandwidthNS + 500) / 1000;
return B_OK;
}