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