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