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