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