xref: /haiku/src/add-ons/kernel/bus_managers/usb/Pipe.cpp (revision 77fb9ca3e653f72d1d15c9f1a50c3d4287f680e0)
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_private.h"
11 
12 
13 Pipe::Pipe(Object *parent)
14 	:	Object(parent),
15 		fDataToggle(false),
16 		fControllerCookie(NULL)
17 {
18 	// all other init is to be done in InitCommon()
19 }
20 
21 
22 Pipe::~Pipe()
23 {
24 	CancelQueuedTransfers(true);
25 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
26 }
27 
28 
29 void
30 Pipe::InitCommon(int8 deviceAddress, uint8 endpointAddress, usb_speed speed,
31 	pipeDirection direction, size_t maxPacketSize, uint8 interval,
32 	int8 hubAddress, uint8 hubPort)
33 {
34 	fDeviceAddress = deviceAddress;
35 	fEndpointAddress = endpointAddress;
36 	fSpeed = speed;
37 	fDirection = direction;
38 	fMaxPacketSize = maxPacketSize;
39 	fInterval = interval;
40 	fHubAddress = hubAddress;
41 	fHubPort = hubPort;
42 
43 	fMaxBurst = 0;
44 	fBytesPerInterval = 0;
45 
46 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_CREATED);
47 }
48 
49 
50 void
51 Pipe::InitSuperSpeed(uint8 maxBurst, uint16 bytesPerInterval)
52 {
53 	fMaxBurst = maxBurst;
54 	fBytesPerInterval = bytesPerInterval;
55 }
56 
57 
58 void
59 Pipe::SetHubInfo(int8 address, uint8 port)
60 {
61 	fHubAddress = address;
62 	fHubPort = port;
63 }
64 
65 
66 status_t
67 Pipe::SubmitTransfer(Transfer *transfer)
68 {
69 	// ToDo: keep track of all submited transfers to be able to cancel them
70 	return GetBusManager()->SubmitTransfer(transfer);
71 }
72 
73 
74 status_t
75 Pipe::CancelQueuedTransfers(bool force)
76 {
77 	return GetBusManager()->CancelQueuedTransfers(this, force);
78 }
79 
80 
81 status_t
82 Pipe::SetFeature(uint16 selector)
83 {
84 	TRACE("set feature %u\n", selector);
85 	Device *device = (Device *)Parent();
86 	if (device->InitCheck() != B_OK)
87 		return B_NO_INIT;
88 
89 	return device->DefaultPipe()->SendRequest(
90 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
91 		USB_REQUEST_SET_FEATURE,
92 		selector,
93 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
94 			: USB_ENDPOINT_ADDR_DIR_OUT),
95 		0,
96 		NULL,
97 		0,
98 		NULL);
99 }
100 
101 
102 status_t
103 Pipe::ClearFeature(uint16 selector)
104 {
105 	Device *device = (Device *)Parent();
106 	if (device->InitCheck() != B_OK)
107 		return B_NO_INIT;
108 
109 	// clearing a stalled condition resets the data toggle
110 	if (selector == USB_FEATURE_ENDPOINT_HALT)
111 		SetDataToggle(false);
112 
113 	TRACE("clear feature %u\n", selector);
114 	return device->DefaultPipe()->SendRequest(
115 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
116 		USB_REQUEST_CLEAR_FEATURE,
117 		selector,
118 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
119 			: USB_ENDPOINT_ADDR_DIR_OUT),
120 		0,
121 		NULL,
122 		0,
123 		NULL);
124 }
125 
126 
127 status_t
128 Pipe::GetStatus(uint16 *status)
129 {
130 	TRACE("get status\n");
131 	Device *device = (Device *)Parent();
132 	if (device->InitCheck() != B_OK)
133 		return B_NO_INIT;
134 
135 	return device->DefaultPipe()->SendRequest(
136 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN,
137 		USB_REQUEST_GET_STATUS,
138 		0,
139 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
140 			: USB_ENDPOINT_ADDR_DIR_OUT),
141 		2,
142 		(void *)status,
143 		2,
144 		NULL);
145 }
146 
147 
148 //
149 // #pragma mark -
150 //
151 
152 
153 InterruptPipe::InterruptPipe(Object *parent)
154 	:	Pipe(parent)
155 {
156 }
157 
158 
159 status_t
160 InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
161 	usb_callback_func callback, void *callbackCookie)
162 {
163 	if (dataLength > 0 && data == NULL)
164 		return B_BAD_VALUE;
165 
166 	Transfer *transfer = new(std::nothrow) Transfer(this);
167 	if (!transfer)
168 		return B_NO_MEMORY;
169 
170 	transfer->SetData((uint8 *)data, dataLength);
171 	transfer->SetCallback(callback, callbackCookie);
172 
173 	status_t result = GetBusManager()->SubmitTransfer(transfer);
174 	if (result < B_OK)
175 		delete transfer;
176 	return result;
177 }
178 
179 
180 //
181 // #pragma mark -
182 //
183 
184 
185 BulkPipe::BulkPipe(Object *parent)
186 	:	Pipe(parent)
187 {
188 }
189 
190 
191 void
192 BulkPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
193 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
194 	uint8 interval, int8 hubAddress, uint8 hubPort)
195 {
196 	// some devices have bogus descriptors
197 	if (speed == USB_SPEED_HIGHSPEED && maxPacketSize != 512)
198 		maxPacketSize = 512;
199 
200 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
201 		maxPacketSize, interval, hubAddress, hubPort);
202 }
203 
204 
205 status_t
206 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
207 	void *callbackCookie)
208 {
209 	if (dataLength > 0 && data == NULL)
210 		return B_BAD_VALUE;
211 
212 	Transfer *transfer = new(std::nothrow) Transfer(this);
213 	if (!transfer)
214 		return B_NO_MEMORY;
215 
216 	transfer->SetData((uint8 *)data, dataLength);
217 	transfer->SetCallback(callback, callbackCookie);
218 
219 	status_t result = GetBusManager()->SubmitTransfer(transfer);
220 	if (result < B_OK)
221 		delete transfer;
222 	return result;
223 }
224 
225 
226 status_t
227 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
228 	usb_callback_func callback, void *callbackCookie, bool physical)
229 {
230 	if (vectorCount > 0 && vector == NULL)
231 		return B_BAD_VALUE;
232 
233 	Transfer *transfer = new(std::nothrow) Transfer(this);
234 	if (!transfer)
235 		return B_NO_MEMORY;
236 
237 	transfer->SetPhysical(physical);
238 	transfer->SetVector(vector, vectorCount);
239 	transfer->SetCallback(callback, callbackCookie);
240 
241 	status_t result = GetBusManager()->SubmitTransfer(transfer);
242 	if (result < B_OK)
243 		delete transfer;
244 	return result;
245 }
246 
247 
248 //
249 // #pragma mark -
250 //
251 
252 
253 IsochronousPipe::IsochronousPipe(Object *parent)
254 	:	Pipe(parent),
255 		fMaxQueuedPackets(0),
256 		fMaxBufferDuration(0),
257 		fSampleSize(0)
258 {
259 }
260 
261 
262 status_t
263 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
264 	usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
265 	uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
266 	void *callbackCookie)
267 {
268 	if ((dataLength > 0 && data == NULL)
269 		|| (packetCount > 0 && packetDesc == NULL))
270 		return B_BAD_VALUE;
271 
272 	usb_isochronous_data *isochronousData
273 		= new(std::nothrow) usb_isochronous_data;
274 
275 	if (!isochronousData)
276 		return B_NO_MEMORY;
277 
278 	isochronousData->packet_descriptors = packetDesc;
279 	isochronousData->packet_count = packetCount;
280 	isochronousData->starting_frame_number = startingFrameNumber;
281 	isochronousData->flags = flags;
282 
283 	Transfer *transfer = new(std::nothrow) Transfer(this);
284 	if (!transfer) {
285 		delete isochronousData;
286 		return B_NO_MEMORY;
287 	}
288 
289 	transfer->SetData((uint8 *)data, dataLength);
290 	transfer->SetCallback(callback, callbackCookie);
291 	transfer->SetIsochronousData(isochronousData);
292 
293 	status_t result = GetBusManager()->SubmitTransfer(transfer);
294 	if (result < B_OK)
295 		delete transfer;
296 	return result;
297 }
298 
299 
300 status_t
301 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
302 	uint16 maxBufferDurationMS, uint16 sampleSize)
303 {
304 	if (maxQueuedPackets == fMaxQueuedPackets
305 		|| maxBufferDurationMS == fMaxBufferDuration
306 		|| sampleSize == fSampleSize)
307 		return B_OK;
308 
309 	fMaxQueuedPackets = maxQueuedPackets;
310 	fMaxBufferDuration = maxBufferDurationMS;
311 	fSampleSize = sampleSize;
312 
313 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
314 	return B_OK;
315 }
316 
317 
318 status_t
319 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
320 	uint16 *maxBufferDurationMS, uint16 *sampleSize)
321 {
322 	if (maxQueuedPackets)
323 		*maxQueuedPackets = fMaxQueuedPackets;
324 	if (maxBufferDurationMS)
325 		*maxBufferDurationMS = fMaxBufferDuration;
326 	if (sampleSize)
327 		*sampleSize = fSampleSize;
328 	return B_OK;
329 }
330 
331 
332 //
333 // #pragma mark -
334 //
335 
336 
337 ControlPipe::ControlPipe(Object *parent)
338 	:	Pipe(parent),
339 		fNotifySem(-1)
340 {
341 	mutex_init(&fSendRequestLock, "control pipe send request");
342 }
343 
344 
345 ControlPipe::~ControlPipe()
346 {
347 	if (fNotifySem >= 0)
348 		delete_sem(fNotifySem);
349 	mutex_lock(&fSendRequestLock);
350 	mutex_destroy(&fSendRequestLock);
351 }
352 
353 
354 void
355 ControlPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
356 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
357 	uint8 interval, int8 hubAddress, uint8 hubPort)
358 {
359 	// The USB 2.0 spec section 5.5.3 gives fixed max packet sizes for the
360 	// different speeds. The USB 3.1 specs defines the max packet size to a
361 	// fixed 512 for control endpoints in 9.6.6. Some devices ignore these
362 	// values and use bogus ones, so we restrict them here.
363 	switch (speed) {
364 		case USB_SPEED_LOWSPEED:
365 			maxPacketSize = 8;
366 			break;
367 		case USB_SPEED_HIGHSPEED:
368 			maxPacketSize = 64;
369 			break;
370 		case USB_SPEED_SUPERSPEED:
371 			maxPacketSize = 512;
372 			break;
373 
374 		default:
375 			break;
376 	}
377 
378 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
379 		maxPacketSize, interval, hubAddress, hubPort);
380 }
381 
382 
383 status_t
384 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
385 	uint16 index, uint16 length, void *data, size_t dataLength,
386 	size_t *actualLength)
387 {
388 	status_t result = mutex_lock(&fSendRequestLock);
389 	if (result != B_OK)
390 		return result;
391 
392 	if (fNotifySem < 0) {
393 		fNotifySem = create_sem(0, "usb send request notify");
394 		if (fNotifySem < 0) {
395 			mutex_unlock(&fSendRequestLock);
396 			return B_NO_MORE_SEMS;
397 		}
398 	}
399 
400 	result = QueueRequest(requestType, request, value, index, length, data,
401 		dataLength, SendRequestCallback, this);
402 	if (result < B_OK) {
403 		mutex_unlock(&fSendRequestLock);
404 		return result;
405 	}
406 
407 	// The sem will be released unconditionally in the callback after the
408 	// result data was filled in. Use a 2 seconds timeout for control transfers.
409 	if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) {
410 		TRACE_ERROR("timeout waiting for queued request to complete\n");
411 
412 		CancelQueuedTransfers(false);
413 
414 		// After the above cancel returns it is guaranteed that the callback
415 		// has been invoked. Therefore we can simply grab that released
416 		// semaphore again to clean up.
417 		acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 0);
418 
419 		if (actualLength)
420 			*actualLength = 0;
421 
422 		mutex_unlock(&fSendRequestLock);
423 		return B_TIMED_OUT;
424 	}
425 
426 	if (actualLength)
427 		*actualLength = fActualLength;
428 
429 	mutex_unlock(&fSendRequestLock);
430 	return fTransferStatus;
431 }
432 
433 
434 void
435 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
436 	size_t actualLength)
437 {
438 	ControlPipe *pipe = (ControlPipe *)cookie;
439 	pipe->fTransferStatus = status;
440 	pipe->fActualLength = actualLength;
441 	release_sem(pipe->fNotifySem);
442 }
443 
444 
445 status_t
446 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
447 	uint16 index, uint16 length, void *data, size_t dataLength,
448 	usb_callback_func callback, void *callbackCookie)
449 {
450 	if (dataLength > 0 && data == NULL)
451 		return B_BAD_VALUE;
452 
453 	usb_request_data *requestData = new(std::nothrow) usb_request_data;
454 	if (!requestData)
455 		return B_NO_MEMORY;
456 
457 	requestData->RequestType = requestType;
458 	requestData->Request = request;
459 	requestData->Value = value;
460 	requestData->Index = index;
461 	requestData->Length = length;
462 
463 	Transfer *transfer = new(std::nothrow) Transfer(this);
464 	if (!transfer) {
465 		delete requestData;
466 		return B_NO_MEMORY;
467 	}
468 
469 	transfer->SetRequestData(requestData);
470 	transfer->SetData((uint8 *)data, dataLength);
471 	transfer->SetCallback(callback, callbackCookie);
472 
473 	status_t result = GetBusManager()->SubmitTransfer(transfer);
474 	if (result < B_OK)
475 		delete transfer;
476 	return result;
477 }
478