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