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
13Pipe::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
22Pipe::~Pipe()
23{
24	CancelQueuedTransfers(true);
25	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
26}
27
28
29void
30Pipe::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
47void
48Pipe::SetHubInfo(int8 address, uint8 port)
49{
50	fHubAddress = address;
51	fHubPort = port;
52}
53
54
55status_t
56Pipe::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
63status_t
64Pipe::CancelQueuedTransfers(bool force)
65{
66	return GetBusManager()->CancelQueuedTransfers(this, force);
67}
68
69
70status_t
71Pipe::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
87status_t
88Pipe::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
108status_t
109Pipe::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
130InterruptPipe::InterruptPipe(Object *parent)
131	:	Pipe(parent)
132{
133}
134
135
136status_t
137InterruptPipe::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
162BulkPipe::BulkPipe(Object *parent)
163	:	Pipe(parent)
164{
165}
166
167
168status_t
169BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
170	void *callbackCookie)
171{
172	if (dataLength > 0 && data == NULL)
173		return B_BAD_VALUE;
174
175	Transfer *transfer = new(std::nothrow) Transfer(this);
176	if (!transfer)
177		return B_NO_MEMORY;
178
179	transfer->SetData((uint8 *)data, dataLength);
180	transfer->SetCallback(callback, callbackCookie);
181
182	status_t result = GetBusManager()->SubmitTransfer(transfer);
183	if (result < B_OK)
184		delete transfer;
185	return result;
186}
187
188
189status_t
190BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
191	usb_callback_func callback, void *callbackCookie, bool physical)
192{
193	if (vectorCount > 0 && vector == NULL)
194		return B_BAD_VALUE;
195
196	Transfer *transfer = new(std::nothrow) Transfer(this);
197	if (!transfer)
198		return B_NO_MEMORY;
199
200	transfer->SetPhysical(physical);
201	transfer->SetVector(vector, vectorCount);
202	transfer->SetCallback(callback, callbackCookie);
203
204	status_t result = GetBusManager()->SubmitTransfer(transfer);
205	if (result < B_OK)
206		delete transfer;
207	return result;
208}
209
210
211//
212// #pragma mark -
213//
214
215
216IsochronousPipe::IsochronousPipe(Object *parent)
217	:	Pipe(parent),
218		fMaxQueuedPackets(0),
219		fMaxBufferDuration(0),
220		fSampleSize(0)
221{
222}
223
224
225status_t
226IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
227	usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
228	uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
229	void *callbackCookie)
230{
231	if ((dataLength > 0 && data == NULL)
232		|| (packetCount > 0 && packetDesc == NULL))
233		return B_BAD_VALUE;
234
235	usb_isochronous_data *isochronousData
236		= new(std::nothrow) usb_isochronous_data;
237
238	if (!isochronousData)
239		return B_NO_MEMORY;
240
241	isochronousData->packet_descriptors = packetDesc;
242	isochronousData->packet_count = packetCount;
243	isochronousData->starting_frame_number = startingFrameNumber;
244	isochronousData->flags = flags;
245
246	Transfer *transfer = new(std::nothrow) Transfer(this);
247	if (!transfer) {
248		delete isochronousData;
249		return B_NO_MEMORY;
250	}
251
252	transfer->SetData((uint8 *)data, dataLength);
253	transfer->SetCallback(callback, callbackCookie);
254	transfer->SetIsochronousData(isochronousData);
255
256	status_t result = GetBusManager()->SubmitTransfer(transfer);
257	if (result < B_OK)
258		delete transfer;
259	return result;
260}
261
262
263status_t
264IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
265	uint16 maxBufferDurationMS, uint16 sampleSize)
266{
267	if (maxQueuedPackets == fMaxQueuedPackets
268		|| maxBufferDurationMS == fMaxBufferDuration
269		|| sampleSize == fSampleSize)
270		return B_OK;
271
272	fMaxQueuedPackets = maxQueuedPackets;
273	fMaxBufferDuration = maxBufferDurationMS;
274	fSampleSize = sampleSize;
275
276	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
277	return B_OK;
278}
279
280
281status_t
282IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
283	uint16 *maxBufferDurationMS, uint16 *sampleSize)
284{
285	if (maxQueuedPackets)
286		*maxQueuedPackets = fMaxQueuedPackets;
287	if (maxBufferDurationMS)
288		*maxBufferDurationMS = fMaxBufferDuration;
289	if (sampleSize)
290		*sampleSize = fSampleSize;
291	return B_OK;
292}
293
294
295//
296// #pragma mark -
297//
298
299
300ControlPipe::ControlPipe(Object *parent)
301	:	Pipe(parent),
302		fNotifySem(-1)
303{
304	mutex_init(&fSendRequestLock, "control pipe send request");
305}
306
307
308ControlPipe::~ControlPipe()
309{
310	if (fNotifySem >= 0)
311		delete_sem(fNotifySem);
312	mutex_lock(&fSendRequestLock);
313	mutex_destroy(&fSendRequestLock);
314}
315
316
317status_t
318ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
319	uint16 index, uint16 length, void *data, size_t dataLength,
320	size_t *actualLength)
321{
322	status_t result = mutex_lock(&fSendRequestLock);
323	if (result != B_OK)
324		return result;
325
326	if (fNotifySem < 0) {
327		fNotifySem = create_sem(0, "usb send request notify");
328		if (fNotifySem < 0) {
329			mutex_unlock(&fSendRequestLock);
330			return B_NO_MORE_SEMS;
331		}
332	}
333
334	result = QueueRequest(requestType, request, value, index, length, data,
335		dataLength, SendRequestCallback, this);
336	if (result < B_OK) {
337		mutex_unlock(&fSendRequestLock);
338		return result;
339	}
340
341	// The sem will be released unconditionally in the callback after the
342	// result data was filled in. Use a 2 seconds timeout for control transfers.
343	if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) {
344		TRACE_ERROR("timeout waiting for queued request to complete\n");
345
346		CancelQueuedTransfers(false);
347
348		// After the above cancel returns it is guaranteed that the callback
349		// has been invoked. Therefore we can simply grab that released
350		// semaphore again to clean up.
351		acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 0);
352
353		if (actualLength)
354			*actualLength = 0;
355
356		mutex_unlock(&fSendRequestLock);
357		return B_TIMED_OUT;
358	}
359
360	if (actualLength)
361		*actualLength = fActualLength;
362
363	mutex_unlock(&fSendRequestLock);
364	return fTransferStatus;
365}
366
367
368void
369ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
370	size_t actualLength)
371{
372	ControlPipe *pipe = (ControlPipe *)cookie;
373	pipe->fTransferStatus = status;
374	pipe->fActualLength = actualLength;
375	release_sem(pipe->fNotifySem);
376}
377
378
379status_t
380ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
381	uint16 index, uint16 length, void *data, size_t dataLength,
382	usb_callback_func callback, void *callbackCookie)
383{
384	if (dataLength > 0 && data == NULL)
385		return B_BAD_VALUE;
386
387	usb_request_data *requestData = new(std::nothrow) usb_request_data;
388	if (!requestData)
389		return B_NO_MEMORY;
390
391	requestData->RequestType = requestType;
392	requestData->Request = request;
393	requestData->Value = value;
394	requestData->Index = index;
395	requestData->Length = length;
396
397	Transfer *transfer = new(std::nothrow) Transfer(this);
398	if (!transfer) {
399		delete requestData;
400		return B_NO_MEMORY;
401	}
402
403	transfer->SetRequestData(requestData);
404	transfer->SetData((uint8 *)data, dataLength);
405	transfer->SetCallback(callback, callbackCookie);
406
407	status_t result = GetBusManager()->SubmitTransfer(transfer);
408	if (result < B_OK)
409		delete transfer;
410	return result;
411}
412