xref: /haiku/src/add-ons/kernel/bus_managers/usb/Transfer.cpp (revision 652f187eee454506a61c85631eb5d8415f3e22d4)
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_p.h"
11 
12 
13 Transfer::Transfer(Pipe *pipe)
14 	:	fPipe(pipe),
15 		fVector(&fData),
16 		fVectorCount(0),
17 		fBaseAddress(NULL),
18 		fFragmented(false),
19 		fActualLength(0),
20 		fUserArea(-1),
21 		fClonedArea(-1),
22 		fCallback(NULL),
23 		fCallbackCookie(NULL),
24 		fRequestData(NULL)
25 {
26 }
27 
28 
29 Transfer::~Transfer()
30 {
31 	// we take ownership of the request data
32 	if (fRequestData)
33 		delete fRequestData;
34 
35 	if (fVector && fVector != &fData)
36 		delete[] fVector;
37 
38 	if (fClonedArea >= B_OK)
39 		delete_area(fClonedArea);
40 }
41 
42 
43 void
44 Transfer::SetRequestData(usb_request_data *data)
45 {
46 	fRequestData = data;
47 }
48 
49 
50 void
51 Transfer::SetData(uint8 *data, size_t dataLength)
52 {
53 	fBaseAddress = data;
54 	fData.iov_base = data;
55 	fData.iov_len = dataLength;
56 
57 	if (data && dataLength > 0)
58 		fVectorCount = 1;
59 }
60 
61 
62 void
63 Transfer::SetVector(iovec *vector, size_t vectorCount)
64 {
65 	fVector = new(std::nothrow) iovec[vectorCount];
66 	memcpy(fVector, vector, vectorCount * sizeof(iovec));
67 	fVectorCount = vectorCount;
68 	fBaseAddress = fVector[0].iov_base;
69 }
70 
71 
72 size_t
73 Transfer::VectorLength()
74 {
75 	size_t length = 0;
76 	for (size_t i = 0; i < fVectorCount; i++)
77 		length += fVector[i].iov_len;
78 
79 	// the data is to large and would overflow the allocator
80 	if (length > USB_MAX_FRAGMENT_SIZE) {
81 		length = USB_MAX_FRAGMENT_SIZE;
82 		fFragmented = true;
83 	}
84 
85 	return length;
86 }
87 
88 
89 void
90 Transfer::AdvanceByFragment(size_t actualLength)
91 {
92 	size_t length = USB_MAX_FRAGMENT_SIZE;
93 	for (size_t i = 0; i < fVectorCount; i++) {
94 		if (fVector[i].iov_len <= length) {
95 			length -= fVector[i].iov_len;
96 			fVector[i].iov_len = 0;
97 			continue;
98 		}
99 
100 		fVector[i].iov_base = (void *)((uint8 *)fVector[i].iov_base + length);
101 		fVector[i].iov_len -= length;
102 		break;
103 	}
104 
105 	fActualLength += actualLength;
106 }
107 
108 
109 status_t
110 Transfer::InitKernelAccess()
111 {
112 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
113 	// we might need to access a buffer in userspace. this will not
114 	// be possible in the kernel space finisher thread unless we
115 	// get the proper area id for the space we need and then clone it
116 	// before reading from or writing to it.
117 	iovec *vector = fVector;
118 	for (size_t i = 0; i < fVectorCount; i++) {
119 		if (IS_USER_ADDRESS(vector[i].iov_base)) {
120 			fUserArea = area_for(vector[i].iov_base);
121 			if (fUserArea < B_OK)
122 				return B_BAD_ADDRESS;
123 			break;
124 		}
125 	}
126 
127 	// no user area required for access
128 	if (fUserArea < B_OK)
129 		return B_OK;
130 
131 	area_info areaInfo;
132 	if (fUserArea < B_OK || get_area_info(fUserArea, &areaInfo) < B_OK)
133 		return B_BAD_ADDRESS;
134 
135 	for (size_t i = 0; i < fVectorCount; i++) {
136 		(uint8 *)vector[i].iov_base -= (uint8 *)areaInfo.address;
137 
138 		if ((size_t)vector[i].iov_base > areaInfo.size
139 			|| (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) {
140 			TRACE_ERROR(("USB Transfer: data buffer spans across multiple areas!\n"));
141 			return B_BAD_ADDRESS;
142 		}
143 	}
144 #endif // !HAIKU_TARGET_PLATFORM_HAIKU
145 
146 	return B_OK;
147 }
148 
149 
150 status_t
151 Transfer::PrepareKernelAccess()
152 {
153 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
154 	// done if there is no userspace buffer or if we already cloned its area
155 	if (fUserArea < B_OK || fClonedArea >= B_OK)
156 		return B_OK;
157 
158 	void *clonedMemory = NULL;
159 	// we got a userspace buffer, need to clone the area for that
160 	// space first and map the iovecs to this cloned area.
161 	fClonedArea = clone_area("userspace accessor", &clonedMemory,
162 		B_ANY_ADDRESS, B_WRITE_AREA | B_KERNEL_WRITE_AREA, fUserArea);
163 
164 	if (fClonedArea < B_OK)
165 		return fClonedArea;
166 
167 	for (size_t i = 0; i < fVectorCount; i++)
168 		(uint8 *)fVector[i].iov_base += (addr_t)clonedMemory;
169 #endif // !HAIKU_TARGET_PLATFORM_HAIKU
170 
171 	return B_OK;
172 }
173 
174 
175 void
176 Transfer::SetCallback(usb_callback_func callback, void *cookie)
177 {
178 	fCallback = callback;
179 	fCallbackCookie = cookie;
180 }
181 
182 
183 void
184 Transfer::Finished(uint32 status, size_t actualLength)
185 {
186 	if (fCallback)
187 		fCallback(fCallbackCookie, status, fBaseAddress,
188 			fActualLength + actualLength);
189 }
190