1 /* 2 * Copyright 2003-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 #include "usb_p.h" 10 11 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 12 #include <kernel.h> 13 #endif 14 15 Transfer::Transfer(Pipe *pipe) 16 : fPipe(pipe), 17 fVector(&fData), 18 fVectorCount(0), 19 fBaseAddress(NULL), 20 fFragmented(false), 21 fActualLength(0), 22 fUserArea(-1), 23 fClonedArea(-1), 24 fCallback(NULL), 25 fCallbackCookie(NULL), 26 fRequestData(NULL), 27 fIsochronousData(NULL), 28 fBandwidth(0) 29 { 30 } 31 32 33 Transfer::~Transfer() 34 { 35 // we take ownership of the request data 36 if (fRequestData) 37 delete fRequestData; 38 39 if (fVector && fVector != &fData) 40 delete[] fVector; 41 42 if (fClonedArea >= B_OK) 43 delete_area(fClonedArea); 44 } 45 46 47 void 48 Transfer::SetRequestData(usb_request_data *data) 49 { 50 fRequestData = data; 51 } 52 53 54 void 55 Transfer::SetIsochronousData(usb_isochronous_data *data) 56 { 57 fIsochronousData = data; 58 } 59 60 61 void 62 Transfer::SetData(uint8 *data, size_t dataLength) 63 { 64 fBaseAddress = data; 65 fData.iov_base = data; 66 fData.iov_len = dataLength; 67 68 if (data && dataLength > 0) 69 fVectorCount = 1; 70 71 // Calculate the bandwidth (only if it is not a bulk transfer) 72 if (!(fPipe->Type() & USB_OBJECT_BULK_PIPE)) { 73 if (_CalculateBandwidth() < B_OK) 74 TRACE_ERROR(("USB Transfer: can't calculate bandwidth\n")); 75 } 76 } 77 78 79 void 80 Transfer::SetVector(iovec *vector, size_t vectorCount) 81 { 82 fVector = new(std::nothrow) iovec[vectorCount]; 83 memcpy(fVector, vector, vectorCount * sizeof(iovec)); 84 fVectorCount = vectorCount; 85 fBaseAddress = fVector[0].iov_base; 86 } 87 88 89 size_t 90 Transfer::VectorLength() 91 { 92 size_t length = 0; 93 for (size_t i = 0; i < fVectorCount; i++) 94 length += fVector[i].iov_len; 95 96 // the data is to large and would overflow the allocator 97 if (length > USB_MAX_FRAGMENT_SIZE) { 98 length = USB_MAX_FRAGMENT_SIZE; 99 fFragmented = true; 100 } 101 102 return length; 103 } 104 105 106 void 107 Transfer::AdvanceByFragment(size_t actualLength) 108 { 109 size_t length = USB_MAX_FRAGMENT_SIZE; 110 for (size_t i = 0; i < fVectorCount; i++) { 111 if (fVector[i].iov_len <= length) { 112 length -= fVector[i].iov_len; 113 fVector[i].iov_len = 0; 114 continue; 115 } 116 117 fVector[i].iov_base = (uint8 *)fVector[i].iov_base + length; 118 fVector[i].iov_len -= length; 119 break; 120 } 121 122 fActualLength += actualLength; 123 } 124 125 126 status_t 127 Transfer::InitKernelAccess() 128 { 129 // we might need to access a buffer in userspace. this will not 130 // be possible in the kernel space finisher thread unless we 131 // get the proper area id for the space we need and then clone it 132 // before reading from or writing to it. 133 iovec *vector = fVector; 134 for (size_t i = 0; i < fVectorCount; i++) { 135 if (IS_USER_ADDRESS(vector[i].iov_base)) { 136 fUserArea = area_for(vector[i].iov_base); 137 if (fUserArea < B_OK) { 138 TRACE_ERROR(("USB Transfer: failed to find area for user" 139 " space buffer!\n")); 140 return B_BAD_ADDRESS; 141 } 142 break; 143 } 144 } 145 146 // no user area required for access 147 if (fUserArea < B_OK) 148 return B_OK; 149 150 area_info areaInfo; 151 if (fUserArea < B_OK || get_area_info(fUserArea, &areaInfo) < B_OK) { 152 TRACE_ERROR(("USB Transfer: couldn't get user area info\n")); 153 return B_BAD_ADDRESS; 154 } 155 156 for (size_t i = 0; i < fVectorCount; i++) { 157 vector[i].iov_base = (uint8 *)vector[i].iov_base - (addr_t)areaInfo.address; 158 159 if ((size_t)vector[i].iov_base > areaInfo.size 160 || (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) { 161 TRACE_ERROR(("USB Transfer: data buffer spans across multiple areas!\n")); 162 return B_BAD_ADDRESS; 163 } 164 } 165 return B_OK; 166 } 167 168 169 status_t 170 Transfer::PrepareKernelAccess() 171 { 172 // done if there is no userspace buffer or if we already cloned its area 173 if (fUserArea < B_OK || fClonedArea >= B_OK) 174 return B_OK; 175 176 void *clonedMemory = NULL; 177 // we got a userspace buffer, need to clone the area for that 178 // space first and map the iovecs to this cloned area. 179 fClonedArea = clone_area("userspace accessor", &clonedMemory, 180 B_ANY_ADDRESS, B_WRITE_AREA | B_KERNEL_WRITE_AREA, fUserArea); 181 182 if (fClonedArea < B_OK) 183 return fClonedArea; 184 185 for (size_t i = 0; i < fVectorCount; i++) 186 fVector[i].iov_base = (uint8 *)fVector[i].iov_base + (addr_t)clonedMemory; 187 return B_OK; 188 } 189 190 191 void 192 Transfer::SetCallback(usb_callback_func callback, void *cookie) 193 { 194 fCallback = callback; 195 fCallbackCookie = cookie; 196 } 197 198 199 void 200 Transfer::Finished(uint32 status, size_t actualLength) 201 { 202 if (fCallback) 203 fCallback(fCallbackCookie, status, fBaseAddress, 204 fActualLength + actualLength); 205 } 206 207 208 /* 209 * USB 2.0 Spec function, pag 64. 210 * This function sets fBandwidth in microsecond 211 * to the bandwidth needed to transfer fData.iov_len bytes. 212 * The calculation is based on 213 * 1. Speed of the transfer 214 * 2. Pipe direction 215 * 3. Type of pipe 216 * 4. Number of bytes to transfer 217 */ 218 status_t 219 Transfer::_CalculateBandwidth() 220 { 221 uint16 bandwidthNS; 222 uint32 type = fPipe->Type(); 223 224 switch (fPipe->Speed()) { 225 case USB_SPEED_HIGHSPEED: 226 { 227 // Direction doesn't matter for highspeed 228 if (type & USB_OBJECT_ISO_PIPE) 229 bandwidthNS = (uint16)((38 * 8 * 2.083) 230 + (2.083 * ((uint32)(3.167 * (1.1667 * 8 * fData.iov_len)))) 231 + USB_BW_HOST_DELAY); 232 else 233 bandwidthNS = (uint16)((55 * 8 * 2.083) 234 + (2.083 * ((uint32)(3.167 * (1.1667 * 8 * fData.iov_len)))) 235 + USB_BW_HOST_DELAY); 236 break; 237 } 238 case USB_SPEED_FULLSPEED: 239 { 240 // Direction does matter this time for isochronous 241 if (type & USB_OBJECT_ISO_PIPE) 242 bandwidthNS = (uint16) 243 (((fPipe->Direction() == Pipe::In) ? 7268 : 6265) 244 + (83.54 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len)))) 245 + USB_BW_HOST_DELAY); 246 else 247 bandwidthNS = (uint16)(9107 248 + (83.54 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len)))) 249 + USB_BW_HOST_DELAY); 250 break; 251 } 252 case USB_SPEED_LOWSPEED: 253 { 254 if (fPipe->Direction() == Pipe::In) 255 bandwidthNS = (uint16) (64060 + (2 * USB_BW_SETUP_LOW_SPEED_PORT_DELAY) 256 + (676.67 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len)))) 257 + USB_BW_HOST_DELAY); 258 else 259 bandwidthNS = (uint16)(64107 + (2 * USB_BW_SETUP_LOW_SPEED_PORT_DELAY) 260 + (667.0 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len)))) 261 + USB_BW_HOST_DELAY); 262 break; 263 } 264 265 default: 266 // We should never get here 267 TRACE(("USB Transfer: speed unknown")); 268 return B_ERROR; 269 } 270 271 // Round up and set the value in microseconds 272 fBandwidth = (bandwidthNS + 500) / 1000; 273 274 return B_OK; 275 } 276