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