xref: /haiku/src/add-ons/kernel/bus_managers/usb/Pipe.cpp (revision d2e1e872611179c9cfaa43ce11bd58b1e3554e4b)
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)
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 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
73 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
74 		USB_REQUEST_SET_FEATURE,
75 		selector,
76 		fEndpointAddress,
77 		0,
78 		NULL,
79 		0,
80 		NULL);
81 }
82 
83 
84 status_t
85 Pipe::ClearFeature(uint16 selector)
86 {
87 	// clearing a stalled condition resets the data toggle
88 	if (selector == USB_FEATURE_ENDPOINT_HALT)
89 		SetDataToggle(false);
90 
91 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
92 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
93 		USB_REQUEST_CLEAR_FEATURE,
94 		selector,
95 		fEndpointAddress,
96 		0,
97 		NULL,
98 		0,
99 		NULL);
100 }
101 
102 
103 status_t
104 Pipe::GetStatus(uint16 *status)
105 {
106 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
107 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN,
108 		USB_REQUEST_GET_STATUS,
109 		0,
110 		fEndpointAddress,
111 		2,
112 		(void *)status,
113 		2,
114 		NULL);
115 }
116 
117 
118 //
119 // #pragma mark -
120 //
121 
122 
123 InterruptPipe::InterruptPipe(Object *parent)
124 	:	Pipe(parent)
125 {
126 }
127 
128 
129 status_t
130 InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
131 	usb_callback_func callback, void *callbackCookie)
132 {
133 	if (dataLength > 0 && data == NULL)
134 		return B_BAD_VALUE;
135 
136 	Transfer *transfer = new(std::nothrow) Transfer(this);
137 	if (!transfer)
138 		return B_NO_MEMORY;
139 
140 	transfer->SetData((uint8 *)data, dataLength);
141 	transfer->SetCallback(callback, callbackCookie);
142 
143 	status_t result = GetBusManager()->SubmitTransfer(transfer);
144 	if (result < B_OK)
145 		delete transfer;
146 	return result;
147 }
148 
149 
150 //
151 // #pragma mark -
152 //
153 
154 
155 BulkPipe::BulkPipe(Object *parent)
156 	:	Pipe(parent)
157 {
158 }
159 
160 
161 status_t
162 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
163 	void *callbackCookie)
164 {
165 	if (dataLength > 0 && data == NULL)
166 		return B_BAD_VALUE;
167 
168 	Transfer *transfer = new(std::nothrow) Transfer(this);
169 	if (!transfer)
170 		return B_NO_MEMORY;
171 
172 	transfer->SetData((uint8 *)data, dataLength);
173 	transfer->SetCallback(callback, callbackCookie);
174 
175 	status_t result = GetBusManager()->SubmitTransfer(transfer);
176 	if (result < B_OK)
177 		delete transfer;
178 	return result;
179 }
180 
181 
182 status_t
183 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
184 	usb_callback_func callback, void *callbackCookie)
185 {
186 	if (vectorCount > 0 && vector == 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->SetVector(vector, vectorCount);
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 //
204 // #pragma mark -
205 //
206 
207 
208 IsochronousPipe::IsochronousPipe(Object *parent)
209 	:	Pipe(parent),
210 		fMaxQueuedPackets(0),
211 		fMaxBufferDuration(0),
212 		fSampleSize(0)
213 {
214 }
215 
216 
217 status_t
218 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
219 	usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
220 	uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
221 	void *callbackCookie)
222 {
223 	if ((dataLength > 0 && data == NULL)
224 		|| (packetCount > 0 && packetDesc == NULL))
225 		return B_BAD_VALUE;
226 
227 	usb_isochronous_data *isochronousData
228 		= new(std::nothrow) usb_isochronous_data;
229 
230 	if (!isochronousData)
231 		return B_NO_MEMORY;
232 
233 	isochronousData->packet_descriptors = packetDesc;
234 	isochronousData->packet_count = packetCount;
235 	isochronousData->starting_frame_number = startingFrameNumber;
236 	isochronousData->flags = flags;
237 
238 	Transfer *transfer = new(std::nothrow) Transfer(this);
239 	if (!transfer) {
240 		delete isochronousData;
241 		return B_NO_MEMORY;
242 	}
243 
244 	transfer->SetData((uint8 *)data, dataLength);
245 	transfer->SetCallback(callback, callbackCookie);
246 	transfer->SetIsochronousData(isochronousData);
247 
248 	status_t result = GetBusManager()->SubmitTransfer(transfer);
249 	if (result < B_OK)
250 		delete transfer;
251 	return result;
252 }
253 
254 
255 status_t
256 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
257 	uint16 maxBufferDurationMS, uint16 sampleSize)
258 {
259 	if (maxQueuedPackets == fMaxQueuedPackets
260 		|| maxBufferDurationMS == fMaxBufferDuration
261 		|| sampleSize == fSampleSize)
262 		return B_OK;
263 
264 	fMaxQueuedPackets = maxQueuedPackets;
265 	fMaxBufferDuration = maxBufferDurationMS;
266 	fSampleSize = sampleSize;
267 
268 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
269 	return B_OK;
270 }
271 
272 
273 status_t
274 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
275 	uint16 *maxBufferDurationMS, uint16 *sampleSize)
276 {
277 	if (maxQueuedPackets)
278 		*maxQueuedPackets = fMaxQueuedPackets;
279 	if (maxBufferDurationMS)
280 		*maxBufferDurationMS = fMaxBufferDuration;
281 	if (sampleSize)
282 		*sampleSize = fSampleSize;
283 	return B_OK;
284 }
285 
286 
287 //
288 // #pragma mark -
289 //
290 
291 
292 typedef struct transfer_result_data_s {
293 	sem_id		notify_sem;
294 	status_t	status;
295 	size_t		actual_length;
296 } transfer_result_data;
297 
298 
299 ControlPipe::ControlPipe(Object *parent)
300 	:	Pipe(parent)
301 {
302 }
303 
304 
305 status_t
306 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
307 	uint16 index, uint16 length, void *data, size_t dataLength,
308 	size_t *actualLength)
309 {
310 	transfer_result_data *transferResult
311 		= new(std::nothrow) transfer_result_data;
312 
313 	transferResult->notify_sem = create_sem(0, "usb send request notify");
314 	if (transferResult->notify_sem < B_OK)
315 		return B_NO_MORE_SEMS;
316 
317 	status_t result = QueueRequest(requestType, request, value, index, length,
318 		data, dataLength, SendRequestCallback, transferResult);
319 	if (result < B_OK) {
320 		delete_sem(transferResult->notify_sem);
321 		return result;
322 	}
323 
324 	// the sem will be released in the callback after the result data was
325 	// filled into the provided struct. use a 1 second timeout to avoid
326 	// hanging applications.
327 	if (acquire_sem_etc(transferResult->notify_sem, 1, B_RELATIVE_TIMEOUT, 1000000) < B_OK) {
328 		TRACE_ERROR(("USB ControlPipe: timeout waiting for queued request to complete\n"));
329 
330 		delete_sem(transferResult->notify_sem);
331 		if (actualLength)
332 			*actualLength = 0;
333 
334 		CancelQueuedTransfers(false);
335 		return B_TIMED_OUT;
336 	}
337 
338 	delete_sem(transferResult->notify_sem);
339 	if (actualLength)
340 		*actualLength = transferResult->actual_length;
341 
342 	result = transferResult->status;
343 	delete transferResult;
344 	return result;
345 }
346 
347 
348 void
349 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
350 	size_t actualLength)
351 {
352 	transfer_result_data *transferResult = (transfer_result_data *)cookie;
353 	transferResult->status = status;
354 	transferResult->actual_length = actualLength;
355 	if (release_sem(transferResult->notify_sem) < B_OK) {
356 		// the request has timed out already - cleanup after us
357 		delete transferResult;
358 	}
359 }
360 
361 
362 status_t
363 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
364 	uint16 index, uint16 length, void *data, size_t dataLength,
365 	usb_callback_func callback, void *callbackCookie)
366 {
367 	if (dataLength > 0 && data == NULL)
368 		return B_BAD_VALUE;
369 
370 	usb_request_data *requestData = new(std::nothrow) usb_request_data;
371 	if (!requestData)
372 		return B_NO_MEMORY;
373 
374 	requestData->RequestType = requestType;
375 	requestData->Request = request;
376 	requestData->Value = value;
377 	requestData->Index = index;
378 	requestData->Length = length;
379 
380 	Transfer *transfer = new(std::nothrow) Transfer(this);
381 	if (!transfer) {
382 		delete requestData;
383 		return B_NO_MEMORY;
384 	}
385 
386 	transfer->SetRequestData(requestData);
387 	transfer->SetData((uint8 *)data, dataLength);
388 	transfer->SetCallback(callback, callbackCookie);
389 
390 	status_t result = GetBusManager()->SubmitTransfer(transfer);
391 	if (result < B_OK)
392 		delete transfer;
393 	return result;
394 }
395