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