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