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