unittests: link libcppunit.so to the unittests directory
[haiku.git] / src / add-ons / kernel / bus_managers / usb / Pipe.cpp
blobf3170285e6c7ce20d7442c77a90743e8438dc94c
1 /*
2 * Copyright 2004-2006, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Michael Lotz <mmlr@mlotz.ch>
7 * Niels S. Reedijk
8 */
10 #include "usb_private.h"
13 Pipe::Pipe(Object *parent)
14 : Object(parent),
15 fDataToggle(false),
16 fControllerCookie(NULL)
18 // all other init is to be done in InitCommon()
22 Pipe::~Pipe()
24 CancelQueuedTransfers(true);
25 GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
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)
34 fDeviceAddress = deviceAddress;
35 fEndpointAddress = endpointAddress;
36 fSpeed = speed;
37 fDirection = direction;
38 fMaxPacketSize = maxPacketSize;
39 fInterval = interval;
40 fHubAddress = hubAddress;
41 fHubPort = hubPort;
43 GetBusManager()->NotifyPipeChange(this, USB_CHANGE_CREATED);
47 void
48 Pipe::SetHubInfo(int8 address, uint8 port)
50 fHubAddress = address;
51 fHubPort = port;
55 status_t
56 Pipe::SubmitTransfer(Transfer *transfer)
58 // ToDo: keep track of all submited transfers to be able to cancel them
59 return GetBusManager()->SubmitTransfer(transfer);
63 status_t
64 Pipe::CancelQueuedTransfers(bool force)
66 return GetBusManager()->CancelQueuedTransfers(this, force);
70 status_t
71 Pipe::SetFeature(uint16 selector)
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),
81 NULL,
83 NULL);
87 status_t
88 Pipe::ClearFeature(uint16 selector)
90 // clearing a stalled condition resets the data toggle
91 if (selector == USB_FEATURE_ENDPOINT_HALT)
92 SetDataToggle(false);
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),
102 NULL,
104 NULL);
108 status_t
109 Pipe::GetStatus(uint16 *status)
111 TRACE("get status\n");
112 return ((Device *)Parent())->DefaultPipe()->SendRequest(
113 USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN,
114 USB_REQUEST_GET_STATUS,
116 fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
117 : USB_ENDPOINT_ADDR_DIR_OUT),
119 (void *)status,
121 NULL);
126 // #pragma mark -
130 InterruptPipe::InterruptPipe(Object *parent)
131 : Pipe(parent)
136 status_t
137 InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
138 usb_callback_func callback, void *callbackCookie)
140 if (dataLength > 0 && data == NULL)
141 return B_BAD_VALUE;
143 Transfer *transfer = new(std::nothrow) Transfer(this);
144 if (!transfer)
145 return B_NO_MEMORY;
147 transfer->SetData((uint8 *)data, dataLength);
148 transfer->SetCallback(callback, callbackCookie);
150 status_t result = GetBusManager()->SubmitTransfer(transfer);
151 if (result < B_OK)
152 delete transfer;
153 return result;
158 // #pragma mark -
162 BulkPipe::BulkPipe(Object *parent)
163 : Pipe(parent)
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)
173 // some devices have bogus descriptors
174 if (speed == USB_SPEED_HIGHSPEED && maxPacketSize != 512)
175 maxPacketSize = 512;
177 Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
178 maxPacketSize, interval, hubAddress, hubPort);
182 status_t
183 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
184 void *callbackCookie)
186 if (dataLength > 0 && data == NULL)
187 return B_BAD_VALUE;
189 Transfer *transfer = new(std::nothrow) Transfer(this);
190 if (!transfer)
191 return B_NO_MEMORY;
193 transfer->SetData((uint8 *)data, dataLength);
194 transfer->SetCallback(callback, callbackCookie);
196 status_t result = GetBusManager()->SubmitTransfer(transfer);
197 if (result < B_OK)
198 delete transfer;
199 return result;
203 status_t
204 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
205 usb_callback_func callback, void *callbackCookie, bool physical)
207 if (vectorCount > 0 && vector == NULL)
208 return B_BAD_VALUE;
210 Transfer *transfer = new(std::nothrow) Transfer(this);
211 if (!transfer)
212 return B_NO_MEMORY;
214 transfer->SetPhysical(physical);
215 transfer->SetVector(vector, vectorCount);
216 transfer->SetCallback(callback, callbackCookie);
218 status_t result = GetBusManager()->SubmitTransfer(transfer);
219 if (result < B_OK)
220 delete transfer;
221 return result;
226 // #pragma mark -
230 IsochronousPipe::IsochronousPipe(Object *parent)
231 : Pipe(parent),
232 fMaxQueuedPackets(0),
233 fMaxBufferDuration(0),
234 fSampleSize(0)
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)
245 if ((dataLength > 0 && data == NULL)
246 || (packetCount > 0 && packetDesc == NULL))
247 return B_BAD_VALUE;
249 usb_isochronous_data *isochronousData
250 = new(std::nothrow) usb_isochronous_data;
252 if (!isochronousData)
253 return B_NO_MEMORY;
255 isochronousData->packet_descriptors = packetDesc;
256 isochronousData->packet_count = packetCount;
257 isochronousData->starting_frame_number = startingFrameNumber;
258 isochronousData->flags = flags;
260 Transfer *transfer = new(std::nothrow) Transfer(this);
261 if (!transfer) {
262 delete isochronousData;
263 return B_NO_MEMORY;
266 transfer->SetData((uint8 *)data, dataLength);
267 transfer->SetCallback(callback, callbackCookie);
268 transfer->SetIsochronousData(isochronousData);
270 status_t result = GetBusManager()->SubmitTransfer(transfer);
271 if (result < B_OK)
272 delete transfer;
273 return result;
277 status_t
278 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
279 uint16 maxBufferDurationMS, uint16 sampleSize)
281 if (maxQueuedPackets == fMaxQueuedPackets
282 || maxBufferDurationMS == fMaxBufferDuration
283 || sampleSize == fSampleSize)
284 return B_OK;
286 fMaxQueuedPackets = maxQueuedPackets;
287 fMaxBufferDuration = maxBufferDurationMS;
288 fSampleSize = sampleSize;
290 GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
291 return B_OK;
295 status_t
296 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
297 uint16 *maxBufferDurationMS, uint16 *sampleSize)
299 if (maxQueuedPackets)
300 *maxQueuedPackets = fMaxQueuedPackets;
301 if (maxBufferDurationMS)
302 *maxBufferDurationMS = fMaxBufferDuration;
303 if (sampleSize)
304 *sampleSize = fSampleSize;
305 return B_OK;
310 // #pragma mark -
314 ControlPipe::ControlPipe(Object *parent)
315 : Pipe(parent),
316 fNotifySem(-1)
318 mutex_init(&fSendRequestLock, "control pipe send request");
322 ControlPipe::~ControlPipe()
324 if (fNotifySem >= 0)
325 delete_sem(fNotifySem);
326 mutex_lock(&fSendRequestLock);
327 mutex_destroy(&fSendRequestLock);
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)
336 status_t result = mutex_lock(&fSendRequestLock);
337 if (result != B_OK)
338 return result;
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;
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;
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");
360 CancelQueuedTransfers(false);
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);
367 if (actualLength)
368 *actualLength = 0;
370 mutex_unlock(&fSendRequestLock);
371 return B_TIMED_OUT;
374 if (actualLength)
375 *actualLength = fActualLength;
377 mutex_unlock(&fSendRequestLock);
378 return fTransferStatus;
382 void
383 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
384 size_t actualLength)
386 ControlPipe *pipe = (ControlPipe *)cookie;
387 pipe->fTransferStatus = status;
388 pipe->fActualLength = actualLength;
389 release_sem(pipe->fNotifySem);
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)
398 if (dataLength > 0 && data == NULL)
399 return B_BAD_VALUE;
401 usb_request_data *requestData = new(std::nothrow) usb_request_data;
402 if (!requestData)
403 return B_NO_MEMORY;
405 requestData->RequestType = requestType;
406 requestData->Request = request;
407 requestData->Value = value;
408 requestData->Index = index;
409 requestData->Length = length;
411 Transfer *transfer = new(std::nothrow) Transfer(this);
412 if (!transfer) {
413 delete requestData;
414 return B_NO_MEMORY;
417 transfer->SetRequestData(requestData);
418 transfer->SetData((uint8 *)data, dataLength);
419 transfer->SetCallback(callback, callbackCookie);
421 status_t result = GetBusManager()->SubmitTransfer(transfer);
422 if (result < B_OK)
423 delete transfer;
424 return result;