xref: /haiku/src/add-ons/kernel/bus_managers/usb/Transfer.cpp (revision 4fd62caa9acc437534c41bbb7d3fc9d53e915005)
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