* Fixed memory leak of Transfers never getting deleted (intentionally until now).

* Reduced the Transfer class to a minimum and removed support for synchronous transfers. The only usage of this is in SendRequest(), which now provides a callback and QueueRequest()s it's request instead.
* Clarified semantics of SubmitTransfer(). It returns a status_t indicating wether the transfer was submitted successfully. This includes no information about the outcome. Therefore the UHCI RootHub now reports the status only through the callback and returns B_OK or B_ERROR for SubmitTransfer().
* Added comment to the Transfer class explaining who takes ownership of the Transfer in which cases.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18511 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2006-08-14 20:54:57 +00:00
parent 49617128ca
commit bf9d24edd2
5 changed files with 97 additions and 162 deletions
+47 -33
View File
@@ -125,7 +125,7 @@ status_t
InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
usb_callback_func callback, void *callbackCookie)
{
Transfer *transfer = new(std::nothrow) Transfer(this, false);
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer)
return B_NO_MEMORY;
@@ -133,10 +133,8 @@ InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
transfer->SetCallback(callback, callbackCookie);
status_t result = SubmitTransfer(transfer);
if (result == B_OK || result == EINPROGRESS)
return B_OK;
delete transfer;
if (result < B_OK)
delete transfer;
return result;
}
@@ -157,7 +155,7 @@ status_t
BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
void *callbackCookie)
{
Transfer *transfer = new(std::nothrow) Transfer(this, false);
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer)
return B_NO_MEMORY;
@@ -165,10 +163,8 @@ BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
transfer->SetCallback(callback, callbackCookie);
status_t result = SubmitTransfer(transfer);
if (result == B_OK || result == EINPROGRESS)
return B_OK;
delete transfer;
if (result < B_OK)
delete transfer;
return result;
}
@@ -199,6 +195,13 @@ IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
//
struct transfer_result_data {
sem_id notify_sem;
uint32 status;
size_t actual_length;
};
ControlPipe::ControlPipe(Device *device, pipeSpeed speed,
uint8 endpointAddress, uint32 maxPacketSize)
: Pipe(device, Pipe::Default, speed, endpointAddress, maxPacketSize)
@@ -230,28 +233,41 @@ ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
uint16 index, uint16 length, void *data, size_t dataLength,
size_t *actualLength)
{
usb_request_data requestData;
requestData.RequestType = requestType;
requestData.Request = request;
requestData.Value = value;
requestData.Index = index;
requestData.Length = length;
transfer_result_data transferResult;
transferResult.notify_sem = create_sem(0, "Send Request Notify Sem");
if (transferResult.notify_sem < B_OK)
return B_NO_MORE_SEMS;
Transfer *transfer = new(std::nothrow) Transfer(this, true);
if (!transfer)
return B_NO_MEMORY;
status_t result = QueueRequest(requestType, request, value, index, length,
data, dataLength, SendRequestCallback, &transferResult);
if (result < B_OK) {
delete_sem(transferResult.notify_sem);
return result;
}
transfer->SetRequestData(&requestData);
transfer->SetData((uint8 *)data, dataLength);
transfer->SetActualLength(actualLength);
// the sem will be released in the callback after
// the result data was filled into the provided struct
acquire_sem(transferResult.notify_sem);
delete_sem(transferResult.notify_sem);
status_t result = SubmitTransfer(transfer);
if (result == EINPROGRESS)
return transfer->WaitForFinish();
if (actualLength)
*actualLength = transferResult.actual_length;
if (result < B_OK)
delete transfer;
return result;
if (transferResult.status == B_USB_STATUS_SUCCESS)
return B_OK;
return B_ERROR;
}
void
ControlPipe::SendRequestCallback(void *cookie, uint32 status, void *data,
size_t actualLength)
{
transfer_result_data *transferResult = (transfer_result_data *)cookie;
transferResult->status = status;
transferResult->actual_length = actualLength;
release_sem(transferResult->notify_sem);
}
@@ -270,7 +286,7 @@ ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
requestData->Index = index;
requestData->Length = length;
Transfer *transfer = new(std::nothrow) Transfer(this, false);
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer) {
delete requestData;
return B_NO_MEMORY;
@@ -281,9 +297,7 @@ ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
transfer->SetCallback(callback, callbackCookie);
status_t result = SubmitTransfer(transfer);
if (result == B_OK || result == EINPROGRESS)
return B_OK;
delete transfer;
if (result < B_OK)
delete transfer;
return result;
}
@@ -9,30 +9,22 @@
#include "usb_p.h"
Transfer::Transfer(Pipe *pipe, bool synchronous)
Transfer::Transfer(Pipe *pipe)
: fPipe(pipe),
fData(NULL),
fDataLength(0),
fActualLengthPointer(NULL),
fActualLength(0),
fStatus(B_USB_STATUS_DRIVER_INTERNAL_ERROR),
fCallback(NULL),
fCallbackCookie(NULL),
fSem(-1),
fHostPrivate(NULL),
fRequestData(NULL)
{
if (synchronous) {
fSem = create_sem(0, "USB Transfer");
set_sem_owner(fSem, B_SYSTEM_TEAM);
}
}
Transfer::~Transfer()
{
if (fSem >= B_OK)
delete_sem(fSem);
// we take ownership of the request data
if (fRequestData)
delete fRequestData;
}
@@ -51,13 +43,6 @@ Transfer::SetData(uint8 *data, size_t dataLength)
}
void
Transfer::SetActualLength(size_t *actualLength)
{
fActualLengthPointer = actualLength;
}
void
Transfer::SetCallback(usb_callback_func callback, void *cookie)
{
@@ -66,47 +51,9 @@ Transfer::SetCallback(usb_callback_func callback, void *cookie)
}
void
Transfer::SetHostPrivate(hostcontroller_priv *priv)
{
fHostPrivate = priv;
}
status_t
Transfer::WaitForFinish()
{
if (fSem < B_OK)
return fSem;
status_t result = acquire_sem(fSem);
if (result < B_OK)
return result;
if (fStatus == B_USB_STATUS_SUCCESS)
return B_OK;
return B_ERROR;
}
void
Transfer::Finished(uint32 status, size_t actualLength)
{
fStatus = status;
fActualLength = actualLength;
if (fActualLengthPointer)
*fActualLengthPointer = actualLength;
// Call the callback function ...
if (fCallback) {
fCallback(fCallbackCookie, fStatus, fData, fActualLength);
return;
}
// ... or release the sem
if (fSem > B_OK) {
release_sem(fSem);
return;
}
if (fCallback)
fCallback(fCallbackCookie, status, fData, actualLength);
}
+13 -25
View File
@@ -266,6 +266,9 @@ virtual void SetDataToggle(bool toggle) {};
uint16 index, uint16 length,
void *data, size_t dataLength,
size_t *actualLength);
static void SendRequestCallback(void *cookie,
uint32 status, void *data,
size_t actualLength);
status_t QueueRequest(uint8 requestType,
uint8 request, uint16 value,
@@ -382,20 +385,19 @@ private:
/*
* This is a forward definition of a struct that is defined in the individual
* host controller modules
*/
struct hostcontroller_priv;
/*
* This class is more like an utility class that performs all functions on
* packets. The class is only used in the bus_manager: the host controllers
* receive the internal data structures.
* A Transfer is allocated on the heap and passed to the Host Controller in
* SubmitTransfer(). It is generated for all queued transfers. If queuing
* succeds (SubmitTransfer() returns with >= B_OK) the Host Controller takes
* ownership of the Transfer and will delete it as soon as it has called the
* set callback function. If SubmitTransfer() failes, the calling function is
* responsible for deleting the Transfer.
* Also, the transfer takes ownership of the usb_request_data passed to it in
* SetRequestData(), but does not take ownership of the data buffer set by
* SetData().
*/
class Transfer {
public:
Transfer(Pipe *pipe, bool synchronous);
Transfer(Pipe *pipe);
~Transfer();
Pipe *TransferPipe() { return fPipe; };
@@ -407,16 +409,9 @@ public:
uint8 *Data() { return fData; };
size_t DataLength() { return fDataLength; };
void SetActualLength(size_t *actualLength);
size_t *ActualLength() { return fActualLengthPointer; };
void SetHostPrivate(hostcontroller_priv *priv);
hostcontroller_priv *HostPrivate() { return fHostPrivate; };
void SetCallback(usb_callback_func callback,
void *cookie);
status_t WaitForFinish();
void Finished(uint32 status, size_t actualLength);
private:
@@ -424,16 +419,9 @@ private:
Pipe *fPipe;
uint8 *fData;
size_t fDataLength;
size_t *fActualLengthPointer;
size_t fActualLength;
uint32 fStatus;
usb_callback_func fCallback;
void *fCallbackCookie;
sem_id fSem;
hostcontroller_priv *fHostPrivate;
// For control transfers
usb_request_data *fRequestData;
};
+3 -2
View File
@@ -583,7 +583,7 @@ UHCI::SubmitTransfer(Transfer *transfer)
return result;
}
return EINPROGRESS;
return B_OK;
}
@@ -655,7 +655,7 @@ UHCI::SubmitRequest(Transfer *transfer)
return result;
}
return EINPROGRESS;
return B_OK;
}
@@ -804,6 +804,7 @@ UHCI::FinishTransfers()
fLastTransfer = lastTransfer;
transfer_data *next = transfer->link;
delete transfer->transfer;
delete transfer;
transfer = next;
+28 -43
View File
@@ -141,18 +141,20 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer)
usb_request_data *request = transfer->RequestData();
TRACE(("usb_uhci_roothub: rh_submit_packet called. request: %u\n", request->Request));
// ToDo: define better status codes. We should return a request error.
uint32 status = B_USB_STATUS_DEVICE_TIMEOUT;
size_t actualLength = 0;
status_t result = B_ERROR;
switch (request->Request) {
case RH_GET_STATUS: {
if (request->Index == 0) {
// Get the hub status -- everything as 0 means all-right
memset(transfer->Data(), 0, sizeof(get_status_buffer));
result = B_OK;
actualLength = MIN(sizeof(get_status_buffer),
transfer->DataLength());
memset(transfer->Data(), 0, actualLength);
status = B_USB_STATUS_SUCCESS;
break;
} else if (request->Index > sUHCIRootHubConfig.hub.num_ports) {
// This port doesn't exist
result = EINVAL;
break;
}
@@ -162,18 +164,18 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer)
actualLength = MIN(4, transfer->DataLength());
memcpy(transfer->Data(),
(void *)&fPortStatus[request->Index - 1], actualLength);
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
}
case RH_SET_ADDRESS:
if (request->Value >= 128) {
result = EINVAL;
status = B_USB_STATUS_DEVICE_TIMEOUT;
break;
}
TRACE(("usb_uhci_roothub: rh_submit_packet RH_ADDRESS: %d\n", request->Value));
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
case RH_GET_DESCRIPTOR:
@@ -185,7 +187,7 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer)
transfer->DataLength());
memcpy(transfer->Data(), (void *)&sUHCIRootHubDevice,
actualLength);
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
}
@@ -194,22 +196,20 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer)
transfer->DataLength());
memcpy(transfer->Data(), (void *)&sUHCIRootHubConfig,
actualLength);
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
}
case RH_STRING_DESCRIPTOR: {
uint8 index = request->Value & 0x00ff;
if (index > 2) {
result = EINVAL;
if (index > 2)
break;
}
actualLength = MIN(sUHCIRootHubStrings[index].length,
transfer->DataLength());
memcpy(transfer->Data(), (void *)&sUHCIRootHubStrings[index],
actualLength);
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
}
@@ -218,48 +218,41 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer)
transfer->DataLength());
memcpy(transfer->Data(), (void *)&sUHCIRootHubConfig.hub,
actualLength);
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
}
default:
result = EINVAL;
break;
}
break;
case RH_SET_CONFIG:
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
case RH_CLEAR_FEATURE: {
if (request->Index == 0) {
// We don't support any hub changes
TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE no hub changes!\n"));
result = EINVAL;
break;
} else if (request->Index > sUHCIRootHubConfig.hub.num_ports) {
// Invalid port number
TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE invalid port!\n"));
result = EINVAL;
break;
}
TRACE(("usb_uhci_roothub: RH_CLEAR_FEATURE called. Feature: %u!\n", request->Value));
uint16 status;
uint16 portStatus;
switch(request->Value) {
case C_PORT_RESET:
fUHCI->SetPortResetChange(request->Index - 1, false);
result = B_OK;
status = B_USB_STATUS_SUCCESS;
break;
case C_PORT_CONNECTION:
status = fUHCI->PortStatus(request->Index - 1);
result = fUHCI->SetPortStatus(request->Index - 1,
(status & UHCI_PORTSC_DATAMASK) | UHCI_PORTSC_STATCHA);
break;
default:
result = EINVAL;
portStatus = fUHCI->PortStatus(request->Index - 1);
if (fUHCI->SetPortStatus(request->Index - 1,
(portStatus & UHCI_PORTSC_DATAMASK)
| UHCI_PORTSC_STATCHA) >= B_OK)
status = B_USB_STATUS_SUCCESS;
break;
}
break;
@@ -269,40 +262,32 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer)
if (request->Index == 0) {
// We don't support any hub changes
TRACE(("usb_uhci_roothub: RH_SET_FEATURE no hub changes!\n"));
result = EINVAL;
break;
} else if (request->Index > sUHCIRootHubConfig.hub.num_ports) {
// Invalid port number
TRACE(("usb_uhci_roothub: RH_SET_FEATURE invalid port!\n"));
result = EINVAL;
break;
}
TRACE(("usb_uhci_roothub: RH_SET_FEATURE called. Feature: %u!\n", request->Value));
switch(request->Value) {
case PORT_RESET:
result = fUHCI->ResetPort(request->Index - 1);
if (fUHCI->ResetPort(request->Index - 1) >= B_OK)
status = B_USB_STATUS_SUCCESS;
break;
case PORT_POWER:
// the ports are automatically powered
result = B_OK;
break;
default:
result = EINVAL;
status = B_USB_STATUS_SUCCESS;
break;
}
break;
}
default:
result = EINVAL;
break;
}
transfer->Finished(result, actualLength);
return result;
transfer->Finished(status, actualLength);
delete transfer;
return B_OK;
}