Completely redesign the USB explore process. Replaces the scary race conditions of the previous locking mechanism and simplifies handling of device changes by a more centralized approach.

Changes are now collected during explore and notifications as well as rescans are done at once. Through this a driver is also not rescanned multiple times anymore.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22929 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2007-11-14 22:11:05 +00:00
parent d3625d72ed
commit 13508e8bd0
13 changed files with 174 additions and 110 deletions

View File

@ -269,7 +269,7 @@ BusManager::SubmitTransfer(Transfer *transfer)
status_t
BusManager::CancelQueuedTransfers(Pipe *pipe)
BusManager::CancelQueuedTransfers(Pipe *pipe, bool force)
{
// virtual function to be overridden
return B_ERROR;

View File

@ -15,6 +15,7 @@ Device::Device(Object *parent, usb_device_descriptor &desc, int8 deviceAddress,
: Object(parent),
fDeviceDescriptor(desc),
fInitOK(false),
fAvailable(true),
fConfigurations(NULL),
fCurrentConfiguration(NULL),
fSpeed(speed),
@ -252,10 +253,29 @@ Device::InitCheck()
}
status_t
Device::Changed(change_item **changeList, bool added)
{
fAvailable = added;
change_item *changeItem = new(std::nothrow) change_item;
if (!changeItem)
return B_NO_MEMORY;
changeItem->added = added;
changeItem->device = this;
changeItem->link = *changeList;
*changeList = changeItem;
return B_OK;
}
status_t
Device::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
void *data, size_t dataLength, size_t *actualLength)
{
if (!fAvailable)
return B_ERROR;
return fDefaultPipe->SendRequest(
USB_REQTYPE_DEVICE_IN | USB_REQTYPE_STANDARD, // type
USB_REQUEST_GET_DESCRIPTOR, // request
@ -304,6 +324,8 @@ Device::SetConfiguration(const usb_configuration_info *configuration)
status_t
Device::SetConfigurationAt(uint8 index)
{
if (!fAvailable)
return B_ERROR;
if (index >= fDeviceDescriptor.num_configurations)
return B_BAD_VALUE;
if (&fConfigurations[index] == fCurrentConfiguration)
@ -382,7 +404,7 @@ Device::Unconfigure(bool atDeviceLevel)
// another configuration unconfigure will be called with
// atDevice = false. otherwise we explicitly want to unconfigure
// the device and have to send it the corresponding request.
if (atDeviceLevel) {
if (atDeviceLevel && fAvailable) {
status_t result = fDefaultPipe->SendRequest(
USB_REQTYPE_DEVICE_OUT | USB_REQTYPE_STANDARD, // type
USB_REQUEST_SET_CONFIGURATION, // request
@ -426,7 +448,7 @@ Device::DeviceDescriptor() const
status_t
Device::ReportDevice(usb_support_descriptor *supportDescriptors,
uint32 supportDescriptorCount, const usb_notify_hooks *hooks,
usb_driver_cookie **cookies, bool added)
usb_driver_cookie **cookies, bool added, bool recursive)
{
TRACE(("USB Device %d: reporting device\n", fDeviceAddress));
bool supported = false;
@ -526,6 +548,9 @@ Device::BuildDeviceName(char *string, uint32 *index, size_t bufferSize,
status_t
Device::SetFeature(uint16 selector)
{
if (!fAvailable)
return B_ERROR;
return fDefaultPipe->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_DEVICE_OUT,
USB_REQUEST_SET_FEATURE,
@ -541,6 +566,9 @@ Device::SetFeature(uint16 selector)
status_t
Device::ClearFeature(uint16 selector)
{
if (!fAvailable)
return B_ERROR;
return fDefaultPipe->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_DEVICE_OUT,
USB_REQUEST_CLEAR_FEATURE,
@ -556,6 +584,9 @@ Device::ClearFeature(uint16 selector)
status_t
Device::GetStatus(uint16 *status)
{
if (!fAvailable)
return B_ERROR;
return fDefaultPipe->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_DEVICE_IN,
USB_REQUEST_GET_STATUS,

View File

@ -30,11 +30,6 @@ Hub::Hub(Object *parent, usb_device_descriptor &desc, int8 deviceAddress,
// Set to false again for the hub init.
fInitOK = false;
if (benaphore_init(&fLock, "usb hub lock") < B_OK) {
TRACE_ERROR(("USB Hub %d: failed to create hub lock\n", DeviceAddress()));
return;
}
if (fDeviceDescriptor.device_class != 9) {
TRACE_ERROR(("USB Hub %d: wrong class! bailing out\n", DeviceAddress()));
return;
@ -97,36 +92,26 @@ Hub::Hub(Object *parent, usb_device_descriptor &desc, int8 deviceAddress,
Hub::~Hub()
{
Lock();
benaphore_destroy(&fLock);
// Remove all child devices
for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
if (!fChildren[i])
continue;
TRACE(("USB Hub %d: removing device 0x%08lx\n", DeviceAddress(), fChildren[i]));
rescan_item *rescanList = NULL;
GetStack()->NotifyDeviceChange(fChildren[i], &rescanList, false);
GetBusManager()->FreeDevice(fChildren[i]);
GetStack()->RescanDrivers(rescanList);
}
delete fInterruptPipe;
}
bool
Hub::Lock()
status_t
Hub::Changed(change_item **changeList, bool added)
{
return (benaphore_lock(&fLock) == B_OK);
}
status_t result = Device::Changed(changeList, added);
if (added || result < B_OK)
return result;
for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
if (fChildren[i] == NULL)
continue;
void
Hub::Unlock()
{
benaphore_unlock(&fLock);
fChildren[i]->Changed(changeList, false);
fChildren[i] = NULL;
}
return B_OK;
}
@ -189,7 +174,7 @@ Hub::ResetPort(uint8 index)
void
Hub::Explore()
Hub::Explore(change_item **changeList)
{
for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
status_t result = UpdatePortStatus(i);
@ -209,7 +194,6 @@ Hub::Explore()
USB_REQUEST_CLEAR_FEATURE, C_PORT_CONNECTION, i + 1,
0, NULL, 0, NULL);
rescan_item *rescanList = NULL;
if (fPortStatus[i].status & PORT_STATUS_CONNECTION) {
// new device attached!
TRACE(("USB Hub %d: new device connected\n", DeviceAddress()));
@ -234,18 +218,8 @@ Hub::Explore()
if (fChildren[i]) {
TRACE_ERROR(("USB Hub %d: new device on a port that is already in use\n", DeviceAddress()));
// Remove previous device first
TRACE(("USB Hub %d: removing device 0x%08lx\n", DeviceAddress(), fChildren[i]));
GetStack()->NotifyDeviceChange(fChildren[i], &rescanList, false);
if (Lock()) {
GetBusManager()->FreeDevice(fChildren[i]);
fChildren[i] = NULL;
Unlock();
}
GetStack()->RescanDrivers(rescanList);
fChildren[i]->Changed(changeList, false);
fChildren[i] = NULL;
}
usb_speed speed = USB_SPEED_FULLSPEED;
@ -256,15 +230,10 @@ Hub::Explore()
Device *newDevice = GetBusManager()->AllocateDevice(this, speed);
if (newDevice && Lock()) {
if (newDevice) {
newDevice->Changed(changeList, true);
fChildren[i] = newDevice;
Unlock();
GetStack()->NotifyDeviceChange(fChildren[i], &rescanList, true);
GetStack()->RescanDrivers(rescanList);
} else {
if (newDevice)
GetBusManager()->FreeDevice(newDevice);
// the device failed to setup correctly, disable the port
// so that the device doesn't get in the way of future
// addressing.
@ -277,15 +246,8 @@ Hub::Explore()
TRACE(("USB Hub %d: device removed\n", DeviceAddress()));
if (fChildren[i]) {
TRACE(("USB Hub %d: removing device 0x%08lx\n", DeviceAddress(), fChildren[i]));
GetStack()->NotifyDeviceChange(fChildren[i], &rescanList, false);
if (Lock()) {
GetBusManager()->FreeDevice(fChildren[i]);
fChildren[i] = NULL;
Unlock();
}
GetStack()->RescanDrivers(rescanList);
fChildren[i]->Changed(changeList, false);
fChildren[i] = NULL;
}
}
}
@ -325,7 +287,7 @@ Hub::Explore()
if (!fChildren[i] || (fChildren[i]->Type() & USB_OBJECT_HUB) == 0)
continue;
((Hub *)fChildren[i])->Explore();
((Hub *)fChildren[i])->Explore(changeList);
}
}
@ -357,28 +319,26 @@ Hub::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
status_t
Hub::ReportDevice(usb_support_descriptor *supportDescriptors,
uint32 supportDescriptorCount, const usb_notify_hooks *hooks,
usb_driver_cookie **cookies, bool added)
usb_driver_cookie **cookies, bool added, bool recursive)
{
TRACE(("USB Hub %d: reporting hub\n", DeviceAddress()));
// Report ourselfs first
status_t result = Device::ReportDevice(supportDescriptors,
supportDescriptorCount, hooks, cookies, added);
supportDescriptorCount, hooks, cookies, added, recursive);
// Then report all of our children
if (!Lock())
return B_ERROR;
if (!recursive)
return result;
for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
if (!fChildren[i])
continue;
if (fChildren[i]->ReportDevice(supportDescriptors,
supportDescriptorCount, hooks, cookies, added) == B_OK)
supportDescriptorCount, hooks, cookies, added, true) == B_OK)
result = B_OK;
}
Unlock();
return result;
}

View File

@ -26,7 +26,7 @@ Pipe::Pipe(Object *parent, int8 deviceAddress, uint8 endpointAddress,
Pipe::~Pipe()
{
CancelQueuedTransfers();
CancelQueuedTransfers(true);
GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
}
@ -40,9 +40,9 @@ Pipe::SubmitTransfer(Transfer *transfer)
status_t
Pipe::CancelQueuedTransfers()
Pipe::CancelQueuedTransfers(bool force)
{
return GetBusManager()->CancelQueuedTransfers(this);
return GetBusManager()->CancelQueuedTransfers(this, force);
}
@ -309,7 +309,7 @@ ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
if (actualLength)
*actualLength = 0;
CancelQueuedTransfers();
CancelQueuedTransfers(false);
return B_TIMED_OUT;
}

View File

@ -28,8 +28,13 @@ Stack::Stack()
{
TRACE(("USB Stack: stack init\n"));
if (benaphore_init(&fLock, "usb stack lock") < B_OK) {
TRACE_ERROR(("USB Stack: failed to create benaphore lock\n"));
if (benaphore_init(&fStackLock, "usb stack lock") < B_OK) {
TRACE_ERROR(("USB Stack: failed to create stack lock\n"));
return;
}
if (benaphore_init(&fExploreLock, "usb explore lock") < B_OK) {
TRACE_ERROR(("USB Stack: failed to create explore lock\n"));
return;
}
@ -85,8 +90,10 @@ Stack::~Stack()
fStopThreads = true;
wait_for_thread(fExploreThread, &result);
Lock();
benaphore_destroy(&fLock);
benaphore_lock(&fStackLock);
benaphore_destroy(&fStackLock);
benaphore_lock(&fExploreLock);
benaphore_destroy(&fExploreLock);
//Release the bus modules
for (Vector<BusManager *>::Iterator i = fBusManagers.Begin();
@ -111,14 +118,14 @@ Stack::InitCheck()
bool
Stack::Lock()
{
return (benaphore_lock(&fLock) == B_OK);
return (benaphore_lock(&fStackLock) == B_OK);
}
void
Stack::Unlock()
{
benaphore_unlock(&fLock);
benaphore_unlock(&fStackLock);
}
@ -189,13 +196,33 @@ Stack::ExploreThread(void *data)
Stack *stack = (Stack *)data;
while (!stack->fStopThreads) {
if (benaphore_lock(&stack->fExploreLock) != B_OK)
break;
rescan_item *rescanList = NULL;
change_item *changeItem = NULL;
for (int32 i = 0; i < stack->fBusManagers.Count(); i++) {
Hub *rootHub = stack->fBusManagers.ElementAt(i)->GetRootHub();
if (rootHub)
rootHub->Explore();
rootHub->Explore(&changeItem);
}
while (changeItem) {
stack->NotifyDeviceChange(changeItem->device, &rescanList, changeItem->added);
if (!changeItem->added) {
// everyone possibly holding a reference is now notified so we
// can delete the device
changeItem->device->GetBusManager()->FreeDevice(changeItem->device);
}
change_item *next = changeItem->link;
delete changeItem;
changeItem = next;
}
stack->fFirstExploreDone = true;
benaphore_unlock(&stack->fExploreLock);
stack->RescanDrivers(rescanList);
snooze(USB_DELAY_HUB_EXPLORE);
}
@ -277,19 +304,33 @@ Stack::NotifyDeviceChange(Device *device, rescan_item **rescanList, bool added)
while (element) {
status_t result = device->ReportDevice(element->support_descriptors,
element->support_descriptor_count, &element->notify_hooks,
&element->cookies, added);
&element->cookies, added, false);
if (result >= B_OK) {
rescan_item *item = new(std::nothrow) rescan_item;
if (!item)
return;
item->name = element->driver_name;
const char *driverName = element->driver_name;
if (element->republish_driver_name)
item->name = element->republish_driver_name;
driverName = element->republish_driver_name;
item->link = *rescanList;
*rescanList = item;
bool already = false;
rescan_item *rescanItem = *rescanList;
while (rescanItem) {
if (strcmp(rescanItem->name, driverName) == 0) {
// this driver is going to be rescanned already
already = true;
break;
}
rescanItem = rescanItem->link;
}
if (!already) {
rescanItem = new(std::nothrow) rescan_item;
if (!rescanItem)
return;
rescanItem->name = driverName;
rescanItem->link = *rescanList;
*rescanList = rescanItem;
}
}
element = element->link;
@ -403,6 +444,9 @@ Stack::InstallNotify(const char *driverName, const usb_notify_hooks *hooks)
usb_driver_info *element = fDriverList;
while (element) {
if (strcmp(element->driver_name, driverName) == 0) {
if (benaphore_lock(&fExploreLock) != B_OK)
return B_ERROR;
// inform driver about any already present devices
for (int32 i = 0; i < fBusManagers.Count(); i++) {
Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
@ -410,12 +454,13 @@ Stack::InstallNotify(const char *driverName, const usb_notify_hooks *hooks)
// Report device will recurse down the whole tree
rootHub->ReportDevice(element->support_descriptors,
element->support_descriptor_count, hooks,
&element->cookies, true);
&element->cookies, true, true);
}
}
element->notify_hooks.device_added = hooks->device_added;
element->notify_hooks.device_removed = hooks->device_removed;
benaphore_unlock(&fExploreLock);
return B_OK;
}
@ -434,17 +479,21 @@ Stack::UninstallNotify(const char *driverName)
usb_driver_info *element = fDriverList;
while (element) {
if (strcmp(element->driver_name, driverName) == 0) {
if (benaphore_lock(&fExploreLock) != B_OK)
return B_ERROR;
// trigger the device removed hook
for (int32 i = 0; i < fBusManagers.Count(); i++) {
Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
if (rootHub)
rootHub->ReportDevice(element->support_descriptors,
element->support_descriptor_count,
&element->notify_hooks, &element->cookies, false);
&element->notify_hooks, &element->cookies, false, true);
}
element->notify_hooks.device_added = NULL;
element->notify_hooks.device_removed = NULL;
benaphore_unlock(&fExploreLock);
return B_OK;
}

View File

@ -318,7 +318,7 @@ cancel_queued_transfers(usb_pipe pipe)
if (!object || (object->Type() & USB_OBJECT_PIPE) == 0)
return B_DEV_INVALID_PIPE;
return ((Pipe *)object)->CancelQueuedTransfers();
return ((Pipe *)object)->CancelQueuedTransfers(false);
}

View File

@ -60,6 +60,13 @@ struct usb_driver_info {
};
struct change_item {
bool added;
Device *device;
change_item *link;
};
struct rescan_item {
const char *name;
rescan_item *link;
@ -141,7 +148,8 @@ static int32 ExploreThread(void *data);
bool fFirstExploreDone;
bool fStopThreads;
benaphore fLock;
benaphore fStackLock;
benaphore fExploreLock;
PhysicalMemoryAllocator *fAllocator;
uint32 fObjectIndex;
@ -178,7 +186,8 @@ virtual status_t Start();
virtual status_t Stop();
virtual status_t SubmitTransfer(Transfer *transfer);
virtual status_t CancelQueuedTransfers(Pipe *pipe);
virtual status_t CancelQueuedTransfers(Pipe *pipe,
bool force);
virtual status_t NotifyPipeChange(Pipe *pipe,
usb_change change);
@ -260,7 +269,7 @@ virtual bool DataToggle() { return fDataToggle; };
virtual void SetDataToggle(bool toggle) { fDataToggle = toggle; };
status_t SubmitTransfer(Transfer *transfer);
status_t CancelQueuedTransfers();
status_t CancelQueuedTransfers(bool force);
// Convenience functions for standard requests
virtual status_t SetFeature(uint16 selector);
@ -414,6 +423,9 @@ virtual ~Device();
status_t InitCheck();
virtual status_t Changed(change_item **changeList,
bool added);
virtual uint32 Type() { return USB_OBJECT_DEVICE; };
ControlPipe *DefaultPipe() { return fDefaultPipe; };
@ -437,7 +449,7 @@ virtual status_t ReportDevice(
uint32 supportDescriptorCount,
const usb_notify_hooks *hooks,
usb_driver_cookie **cookies,
bool added);
bool added, bool recursive);
virtual status_t BuildDeviceName(char *string,
uint32 *index, size_t bufferSize,
Device *device);
@ -452,6 +464,7 @@ protected:
bool fInitOK;
private:
bool fAvailable;
usb_configuration_info *fConfigurations;
usb_configuration_info *fCurrentConfiguration;
usb_speed fSpeed;
@ -468,8 +481,8 @@ public:
usb_speed speed);
virtual ~Hub();
bool Lock();
void Unlock();
virtual status_t Changed(change_item **changeList,
bool added);
virtual uint32 Type() { return USB_OBJECT_DEVICE | USB_OBJECT_HUB; };
@ -480,7 +493,7 @@ virtual status_t GetDescriptor(uint8 descriptorType,
status_t UpdatePortStatus(uint8 index);
status_t ResetPort(uint8 index);
void Explore();
void Explore(change_item **changeList);
static void InterruptCallback(void *cookie,
status_t status, void *data,
size_t actualLength);
@ -490,14 +503,12 @@ virtual status_t ReportDevice(
uint32 supportDescriptorCount,
const usb_notify_hooks *hooks,
usb_driver_cookie **cookies,
bool added);
bool added, bool recursive);
virtual status_t BuildDeviceName(char *string,
uint32 *index, size_t bufferSize,
Device *device);
private:
benaphore fLock;
InterruptPipe *fInterruptPipe;
usb_hub_descriptor fHubDescriptor;

View File

@ -864,7 +864,7 @@ EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead,
status_t
EHCI::CancelQueuedTransfers(Pipe *pipe)
EHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
{
if (!Lock())
return B_ERROR;
@ -879,7 +879,13 @@ EHCI::CancelQueuedTransfers(Pipe *pipe)
descriptor = (ehci_qtd *)descriptor->next_log;
}
current->transfer->Finished(B_CANCELED, 0);
if (!force) {
// if the transfer is canceled by force, the one causing the
// cancel is probably not the one who initiated the transfer
// and the callback is likely not safe anymore
current->transfer->Finished(B_CANCELED, 0);
}
current->canceled = true;
}

View File

@ -37,7 +37,7 @@ public:
virtual status_t SubmitTransfer(Transfer *transfer);
status_t SubmitPeriodicTransfer(Transfer *transfer);
status_t SubmitAsyncTransfer(Transfer *transfer);
virtual status_t CancelQueuedTransfers(Pipe *pipe);
virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force);
virtual status_t NotifyPipeChange(Pipe *pipe,
usb_change change);

View File

@ -791,7 +791,7 @@ OHCI::_ReadReg(uint32 reg)
status_t
OHCI::CancelQueuedTransfers(Pipe *pipe)
OHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
{
return B_ERROR;
}

View File

@ -57,7 +57,8 @@ public:
status_t Start();
virtual status_t SubmitTransfer(Transfer *transfer);
virtual status_t CancelQueuedTransfers(Pipe *pipe);
virtual status_t CancelQueuedTransfers(Pipe *pipe,
bool force);
status_t SubmitRequest(Transfer *transfer);
virtual status_t NotifyPipeChange(Pipe *pipe,

View File

@ -603,7 +603,7 @@ UHCI::SubmitTransfer(Transfer *transfer)
status_t
UHCI::CancelQueuedTransfers(Pipe *pipe)
UHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
{
if (pipe->Type() & USB_OBJECT_ISO_PIPE)
return CancelQueuedIsochronousTransfers(pipe);
@ -621,7 +621,13 @@ UHCI::CancelQueuedTransfers(Pipe *pipe)
descriptor = (uhci_td *)descriptor->link_log;
}
current->transfer->Finished(B_CANCELED, 0);
if (!force) {
// if the transfer is canceled by force, the one causing the
// cancel is probably not the one who initiated the transfer
// and the callback is likely not safe anymore
current->transfer->Finished(B_CANCELED, 0);
}
current->canceled = true;
}
current = current->link;

View File

@ -91,7 +91,7 @@ public:
status_t Start();
virtual status_t SubmitTransfer(Transfer *transfer);
virtual status_t CancelQueuedTransfers(Pipe *pipe);
virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force);
status_t CancelQueuedIsochronousTransfers(Pipe *pipe);
status_t SubmitRequest(Transfer *transfer);
status_t SubmitIsochronous(Transfer *transfer);