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