xref: /haiku/src/add-ons/kernel/bus_managers/usb/Pipe.cpp (revision b028e77473189065f2baefc6f5e10d451cf591e2)
1 /*
2  * Copyright 2004-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 Pipe::Pipe(Object *parent, int8 deviceAddress, uint8 endpointAddress,
14 	pipeDirection direction, usb_speed speed, size_t maxPacketSize)
15 	:	Object(parent),
16 		fDeviceAddress(deviceAddress),
17 		fEndpointAddress(endpointAddress),
18 		fDirection(direction),
19 		fSpeed(speed),
20 		fMaxPacketSize(maxPacketSize),
21 		fDataToggle(false)
22 {
23 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_CREATED);
24 }
25 
26 
27 Pipe::~Pipe()
28 {
29 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
30 }
31 
32 
33 status_t
34 Pipe::SubmitTransfer(Transfer *transfer)
35 {
36 	// ToDo: keep track of all submited transfers to be able to cancel them
37 	return GetBusManager()->SubmitTransfer(transfer);
38 }
39 
40 
41 status_t
42 Pipe::CancelQueuedTransfers()
43 {
44 	return GetBusManager()->CancelQueuedTransfers(this);
45 }
46 
47 
48 status_t
49 Pipe::SetFeature(uint16 selector)
50 {
51 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
52 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
53 		USB_REQUEST_SET_FEATURE,
54 		selector,
55 		fEndpointAddress,
56 		0,
57 		NULL,
58 		0,
59 		NULL);
60 }
61 
62 
63 status_t
64 Pipe::ClearFeature(uint16 selector)
65 {
66 	// clearing a stalled condition resets the data toggle
67 	if (selector == USB_FEATURE_ENDPOINT_HALT)
68 		SetDataToggle(false);
69 
70 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
71 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
72 		USB_REQUEST_CLEAR_FEATURE,
73 		selector,
74 		fEndpointAddress,
75 		0,
76 		NULL,
77 		0,
78 		NULL);
79 }
80 
81 
82 status_t
83 Pipe::GetStatus(uint16 *status)
84 {
85 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
86 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN,
87 		USB_REQUEST_GET_STATUS,
88 		0,
89 		fEndpointAddress,
90 		2,
91 		(void *)status,
92 		2,
93 		NULL);
94 }
95 
96 
97 //
98 // #pragma mark -
99 //
100 
101 
102 InterruptPipe::InterruptPipe(Object *parent, int8 deviceAddress,
103 	uint8 endpointAddress, pipeDirection direction, usb_speed speed,
104 	size_t maxPacketSize)
105 	:	Pipe(parent, deviceAddress, endpointAddress, direction, speed,
106 			maxPacketSize)
107 {
108 }
109 
110 
111 status_t
112 InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
113 	usb_callback_func callback, void *callbackCookie)
114 {
115 	Transfer *transfer = new(std::nothrow) Transfer(this);
116 	if (!transfer)
117 		return B_NO_MEMORY;
118 
119 	transfer->SetData((uint8 *)data, dataLength);
120 	transfer->SetCallback(callback, callbackCookie);
121 
122 	status_t result = GetBusManager()->SubmitTransfer(transfer);
123 	if (result < B_OK)
124 		delete transfer;
125 	return result;
126 }
127 
128 
129 //
130 // #pragma mark -
131 //
132 
133 
134 BulkPipe::BulkPipe(Object *parent, int8 deviceAddress, uint8 endpointAddress,
135 	pipeDirection direction, usb_speed speed, size_t maxPacketSize)
136 	:	Pipe(parent, deviceAddress, endpointAddress, direction, speed,
137 			maxPacketSize)
138 {
139 }
140 
141 
142 status_t
143 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
144 	void *callbackCookie)
145 {
146 	Transfer *transfer = new(std::nothrow) Transfer(this);
147 	if (!transfer)
148 		return B_NO_MEMORY;
149 
150 	transfer->SetData((uint8 *)data, dataLength);
151 	transfer->SetCallback(callback, callbackCookie);
152 
153 	status_t result = GetBusManager()->SubmitTransfer(transfer);
154 	if (result < B_OK)
155 		delete transfer;
156 	return result;
157 }
158 
159 
160 status_t
161 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
162 	usb_callback_func callback, void *callbackCookie)
163 {
164 	Transfer *transfer = new(std::nothrow) Transfer(this);
165 	if (!transfer)
166 		return B_NO_MEMORY;
167 
168 	transfer->SetVector(vector, vectorCount);
169 	transfer->SetCallback(callback, callbackCookie);
170 
171 	status_t result = GetBusManager()->SubmitTransfer(transfer);
172 	if (result < B_OK)
173 		delete transfer;
174 	return result;
175 }
176 
177 
178 //
179 // #pragma mark -
180 //
181 
182 
183 IsochronousPipe::IsochronousPipe(Object *parent, int8 deviceAddress,
184 	uint8 endpointAddress, pipeDirection direction, usb_speed speed,
185 	size_t maxPacketSize)
186 	:	Pipe(parent, deviceAddress, endpointAddress, direction, speed,
187 			maxPacketSize),
188 		fMaxQueuedPackets(0),
189 		fMaxBufferDuration(0),
190 		fSampleSize(0)
191 {
192 }
193 
194 
195 status_t
196 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
197 	usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
198 	uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
199 	void *callbackCookie)
200 {
201 	// TODO: Check if values of input parameters are set correctely
202 	usb_isochronous_data *isochronousData
203 		= new(std::nothrow) usb_isochronous_data;
204 
205 	if (!isochronousData)
206 		return B_NO_MEMORY;
207 
208 	isochronousData->packet_descriptors = packetDesc;
209 	isochronousData->packet_count = packetCount;
210 	isochronousData->starting_frame_number = startingFrameNumber;
211 	isochronousData->flags = flags;
212 
213 	Transfer *transfer = new(std::nothrow) Transfer(this);
214 	if (!transfer) {
215 		delete isochronousData;
216 		return B_NO_MEMORY;
217 	}
218 
219 	transfer->SetData((uint8 *)data, dataLength);
220 	transfer->SetCallback(callback, callbackCookie);
221 	transfer->SetIsochronousData(isochronousData);
222 
223 	status_t result = GetBusManager()->SubmitTransfer(transfer);
224 	if (result < B_OK)
225 		delete transfer;
226 	return result;
227 }
228 
229 
230 status_t
231 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
232 	uint16 maxBufferDurationMS, uint16 sampleSize)
233 {
234 	if (maxQueuedPackets == fMaxQueuedPackets
235 		|| maxBufferDurationMS == fMaxBufferDuration
236 		|| sampleSize == fSampleSize)
237 		return B_OK;
238 
239 	fMaxQueuedPackets = maxQueuedPackets;
240 	fMaxBufferDuration = maxBufferDurationMS;
241 	fSampleSize = sampleSize;
242 
243 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
244 	return B_OK;
245 }
246 
247 
248 status_t
249 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
250 	uint16 *maxBufferDurationMS, uint16 *sampleSize)
251 {
252 	if (maxQueuedPackets)
253 		*maxQueuedPackets = fMaxQueuedPackets;
254 	if (maxBufferDurationMS)
255 		*maxBufferDurationMS = fMaxBufferDuration;
256 	if (sampleSize)
257 		*sampleSize = fSampleSize;
258 	return B_OK;
259 }
260 
261 
262 //
263 // #pragma mark -
264 //
265 
266 
267 typedef struct transfer_result_data_s {
268 	sem_id		notify_sem;
269 	status_t	status;
270 	size_t		actual_length;
271 } transfer_result_data;
272 
273 
274 ControlPipe::ControlPipe(Object *parent, int8 deviceAddress,
275 	uint8 endpointAddress, usb_speed speed, size_t maxPacketSize)
276 	:	Pipe(parent, deviceAddress, endpointAddress, Default, speed,
277 			maxPacketSize)
278 {
279 }
280 
281 
282 status_t
283 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
284 	uint16 index, uint16 length, void *data, size_t dataLength,
285 	size_t *actualLength)
286 {
287 	transfer_result_data *transferResult
288 		= new(std::nothrow) transfer_result_data;
289 
290 	transferResult->notify_sem = create_sem(0, "usb send request notify");
291 	if (transferResult->notify_sem < B_OK)
292 		return B_NO_MORE_SEMS;
293 
294 	status_t result = QueueRequest(requestType, request, value, index, length,
295 		data, dataLength, SendRequestCallback, transferResult);
296 	if (result < B_OK) {
297 		delete_sem(transferResult->notify_sem);
298 		return result;
299 	}
300 
301 	// the sem will be released in the callback after the result data was
302 	// filled into the provided struct. use a 5 seconds timeout to avoid
303 	// hanging applications.
304 	if (acquire_sem_etc(transferResult->notify_sem, 1, B_RELATIVE_TIMEOUT, 5000000) < B_OK) {
305 		TRACE_ERROR(("USB ControlPipe: timeout waiting for queued request to complete\n"));
306 
307 		delete_sem(transferResult->notify_sem);
308 		if (actualLength)
309 			*actualLength = 0;
310 
311 		// ToDo: cancel the transfer at the bus manager
312 		return B_TIMED_OUT;
313 	}
314 
315 	delete_sem(transferResult->notify_sem);
316 	if (actualLength)
317 		*actualLength = transferResult->actual_length;
318 
319 	result = transferResult->status;
320 	delete transferResult;
321 	return result;
322 }
323 
324 
325 void
326 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
327 	size_t actualLength)
328 {
329 	transfer_result_data *transferResult = (transfer_result_data *)cookie;
330 	transferResult->status = status;
331 	transferResult->actual_length = actualLength;
332 	if (release_sem(transferResult->notify_sem) < B_OK) {
333 		// the request has timed out already - cleanup after us
334 		delete transferResult;
335 	}
336 }
337 
338 
339 status_t
340 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
341 	uint16 index, uint16 length, void *data, size_t dataLength,
342 	usb_callback_func callback, void *callbackCookie)
343 {
344 	usb_request_data *requestData = new(std::nothrow) usb_request_data;
345 	if (!requestData)
346 		return B_NO_MEMORY;
347 
348 	requestData->RequestType = requestType;
349 	requestData->Request = request;
350 	requestData->Value = value;
351 	requestData->Index = index;
352 	requestData->Length = length;
353 
354 	Transfer *transfer = new(std::nothrow) Transfer(this);
355 	if (!transfer) {
356 		delete requestData;
357 		return B_NO_MEMORY;
358 	}
359 
360 	transfer->SetRequestData(requestData);
361 	transfer->SetData((uint8 *)data, dataLength);
362 	transfer->SetCallback(callback, callbackCookie);
363 
364 	status_t result = GetBusManager()->SubmitTransfer(transfer);
365 	if (result < B_OK)
366 		delete transfer;
367 	return result;
368 }
369