/**
 * \brief Submit a bulk transfer to the device.
 *
 * This method allocates and fills the bulk transfer, and submits it to
 * the libusb_submit_transfer function. It then starts to handle events,
 * and only returns when the complete flag is set.
 * \param dev_handle    the libusb device handle
 */
void	BulkTransfer::submit(libusb_device_handle *dev_handle) throw(USBError) {
    // allocate the transfer structure
    transfer = libusb_alloc_transfer(0);

    // fill the transfer
    libusb_fill_bulk_transfer(transfer, dev_handle,
                              endpoint->bEndpointAddress(), data,
                              length, bulktransfer_callback, this, timeout);

    // submit the transfer
    debug(LOG_DEBUG, DEBUG_LOG, 0, "submitting bulk transfer, timeout = %d",
          timeout);
    int	rc = libusb_submit_transfer(transfer);
    if (rc != LIBUSB_SUCCESS) {
        throw USBError(libusb_error_name(rc));
    }

    // handle events until the complete flag is set
    libusb_context  *ctx = getContext();
    while (!complete) {
        libusb_handle_events(ctx);
    }

    // at this point, the transfer has somehow completed, but we
    // don't know yet what happened.
    const char	*cause = usb_status_name(transfer->status);
    if (NULL != cause) {
        debug(LOG_ERR, DEBUG_LOG, 0, "transfer failed: %s", cause);
        throw USBError(cause);
    } else {
        debug(LOG_DEBUG, DEBUG_LOG, 0, "transfer complete, %d bytes",
              transfer->actual_length);
    }
}
/**
 * \brief Submit a bulk transfer to the device.
 *
 * This method allocates and fills the bulk transfer, and submits it to
 * the libusb_submit_transfer function. It then starts to handle events,
 * and only returns when the complete flag is set.
 * \param dev_handle    the libusb device handle
 */
void	BulkTransfer::submit(libusb_device_handle *dev_handle) throw(USBError) {
	// allocate the transfer structure
	transfer = libusb_alloc_transfer(0);

	// fill the transfer
	libusb_fill_bulk_transfer(transfer, dev_handle,
		endpoint->bEndpointAddress(), data,
		length, bulktransfer_callback, this, timeout);

	// submit the transfer
	int	rc = libusb_submit_transfer(transfer);
	if (rc != LIBUSB_SUCCESS) {
		throw USBError(libusb_error_name(rc));
	}

	// handle events until the complete flag is set
	libusb_context  *ctx = getContext();
	while (!complete) {
		libusb_handle_events(ctx);
	}

	// at this point, the transfer has somehow completed, but we
	// don't know yet what happened.
	const char	*cause = NULL;
	switch (transfer->status) {
	case LIBUSB_TRANSFER_ERROR:
		cause = "transfer error";
		break;
	case LIBUSB_TRANSFER_TIMED_OUT:
		cause = "transfer timed out";
		break;
	case LIBUSB_TRANSFER_CANCELLED:
		cause = "transfer cancelled";
		break;
	case LIBUSB_TRANSFER_STALL:
		cause = "transfer stall";
		break;
	case LIBUSB_TRANSFER_NO_DEVICE:
		cause = "transfer no device";
		break;
	case LIBUSB_TRANSFER_OVERFLOW:
		cause = "transfer overflow";
		break;
	case LIBUSB_TRANSFER_COMPLETED:
		break;
	}
	if (NULL != cause) {
		debug(LOG_ERR, DEBUG_LOG, 0, "transfer failed: %s", cause);
		throw USBError(cause);
	} else {
		debug(LOG_DEBUG, DEBUG_LOG, 0, "transfer complete, %d bytes",
			transfer->actual_length);
	}
}
/**
 * \brief Get active configuaration descriptor.
 *
 * This method returns the active device descriptor. The device has to
 * be open for this to work. This is a restriction imposed by a bug in
 * libusb-1.0: on Mac OS X, the library causes a segmentation fault when
 * trying to retrieve the active configuration descriptor of a device that
 * was not opened. Although it works on other platforms (e.g. Linux),
 * by enforcing this restriction on all platforms we ensure that code
 * in Linux cannot inadvertently trigger this bug and cause segementation
 * faults on Mac OS X.
 */
ConfigurationPtr	Device::activeConfig() throw(USBError) {
	if (!isOpen()) {
		throw USBError("device not open");
	}
	struct libusb_config_descriptor	*config = NULL;
	int	rc = libusb_get_active_config_descriptor(dev, &config);
	if (rc != LIBUSB_SUCCESS) {
		throw USBError(libusb_error_name(rc));
	}
	Configuration	*result = new Configuration(*this, config);
	libusb_free_config_descriptor(config);
	return ConfigurationPtr(result);
}
bool	Device::kernelDriverActive(uint8_t interface) const throw(USBError) {
	int	rc = libusb_kernel_driver_active(dev_handle, interface);
	if (rc < 0) {
		throw USBError(libusb_error_name(rc));
	}
	return (rc) ? true : false;
}
/**
 * \brief Select a configuration by number
 *
 * \param configuration	number of the configuration to select.
 */
void	Device::setConfiguration(uint8_t configuration) throw (USBError) {
	int	rc = libusb_set_configuration(dev_handle, configuration);
	if (rc != LIBUSB_SUCCESS) {
		debug(LOG_ERR, DEBUG_LOG, 0, "cannot set configuration %d: %s",
			configuration, libusb_error_name(rc));
		throw USBError(libusb_error_name(rc));
	}
}
void	Device::attachKernelDriver(uint8_t interface) const throw(USBError) {
	int	rc = libusb_attach_kernel_driver(dev_handle, interface);
	if (rc < 0) {
		debug(LOG_ERR, DEBUG_LOG, 0, "cannot attach kernel driver: %s",
			libusb_error_name(rc));
		throw USBError(libusb_error_name(rc));
	}
}
void	Device::claimInterface(uint8_t interface) throw(USBError) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "claiming interface %d", interface);
	int	rc = libusb_claim_interface(dev_handle, interface);
	if (rc != LIBUSB_SUCCESS) {
		debug(LOG_ERR, DEBUG_LOG, 0, "cannot claim interface %d: %s",
			interface, libusb_error_name(rc));
		throw USBError(libusb_error_name(rc));
	}
}
/**
 * \brief Get configuration by value.
 *
 * \param value	configuration value to search for.
 */
ConfigurationPtr	Device::configValue(uint8_t value) throw(USBError) {
	struct libusb_config_descriptor	*config;
	int	rc = libusb_get_config_descriptor_by_value(dev, value, &config);
	if (rc != LIBUSB_SUCCESS) {
		throw USBError(libusb_error_name(rc));
	}
	Configuration	*result = new Configuration(*this, config);
	libusb_free_config_descriptor(config);
	return ConfigurationPtr(result);
}
/**
 * \brief Execute a control request.
 *
 * All information necessary to execute a control request to the device
 * is contained in the request argument. This method just sends the
 * contents of the request to the device. If the request includes
 * a data phase, then the direction of the data phase was encoded in
 * the request when the request was constructed.
 *
 * \param request	pointer to the control request
 */
void	Device::controlRequest(RequestBase *request) throw(USBError) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "bmRequest = %02x, bRequest = %02x, "
		"wValue = %04x, wIndex = %04x, wLength = %d",
		request->bmRequestType(), request->bRequest(),
		request->wValue(), request->wIndex(), request->wLength());

	// for debugging, display the request content if it is going to
	// the host
	if ((request->bmRequestType() & 0x80) == RequestBase::host_to_device) {
		debug(LOG_DEBUG, DEBUG_LOG, 0, "payload to send:\n%s",
			request->payloadHex().c_str());
	}

	int	rc = libusb_control_transfer(dev_handle, 
			request->bmRequestType(),
			request->bRequest(),
			request->wValue(),
			request->wIndex(),
			request->payload(),
			request->wLength(),
			request->getTimeout());
	debug(LOG_DEBUG, DEBUG_LOG, 0, "control request result: %d", rc);
	if (rc < 0) {
		throw USBError(libusb_error_name(rc));
	}

	// for debuggung: if the data phase goes from device to host, display
	// the response
	if ((request->bmRequestType() & 0x80) == RequestBase::device_to_host) {
		debug(LOG_DEBUG, DEBUG_LOG, 0, "payload received:\n%s",
			request->payloadHex().c_str());
	}

	// only accept the response if it has the right length, otherwise
	// throw an exception
	if ((rc != request->wLength()) && (!request->accept_short_response)) {
		std::string	message
			= stringprintf("expecting %d bytes, %d received",
				request->wLength(), rc);
		std::cerr << request->payloadHex();
		throw USBError(message.c_str());
	}
}
/**
 * \brief Select an alternate setting on an interface.
 *
 * 
 * \param interface	interface number
 * \param altsetting	number of alternate setting
 */
void	Device::setInterfaceAltSetting(uint8_t interface, uint8_t altsetting)
		throw(USBError) {
	int	rc = libusb_set_interface_alt_setting(dev_handle,
			interface, altsetting);
	if (rc != LIBUSB_SUCCESS) {
		debug(LOG_ERR, DEBUG_LOG, 0, "cannot set altsetting %d: %s",
			altsetting, libusb_error_name(rc));
		throw USBError(libusb_error_name(rc));
	}
}
/**
 * \brief Get the number of the current configuration.
 *
 * \return configuration number
 */
int	Device::getConfiguration() throw(USBError) {
	int	result;
	int	rc = libusb_get_configuration(dev_handle, &result);
	if (rc != LIBUSB_SUCCESS) {
		debug(LOG_ERR, DEBUG_LOG, 0, "cannot get configuration: %s",
			libusb_error_name(rc));
		throw USBError(libusb_error_name(rc));
	}
	return result;
}
/**
 * \brief Compute which is the preferred alternate setting for this interface
 *
 * \param interface	interface number of the streaming interface
 */
int	UVCCamera::preferredAltSetting(uint8_t interface) {
	// get the currently negotiated settings
	getCur(interface);

	// if the frame interval is 0, we have to ask the format for
	// the default frame interval
	if (0 == frameinterval) {
		debug(LOG_WARNING, DEBUG_LOG, 0,
			"warning: no negotiated frame interval");
		frameinterval = 333333;
	}

	// compute the data rate
	double	datarate = maxvideoframesize * (10000000. / frameinterval);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "required data rate: %.1fMBps",
		datarate / 1000000.);

	// for this software, bulk transfers are preferable, if the
	// device supports them, so we check whether alt setting 0 has a
	// bulk endpoint, and return 0 in that case
	InterfacePtr	interfaceptr = (*device.activeConfig())[interface];
	InterfaceDescriptorPtr	ifdescptr = (*interfaceptr)[0];
	if (ifdescptr->numEndpoints() > 0) {
		EndpointDescriptorPtr	endpointptr = (*ifdescptr)[0];
		if (endpointptr->isBulk()) {
			return 0;
		}
	}

	// if there was no bulk endpoint, then we have to find an alternate
	// setting that provides enough bandwidth. So we go through all
	// the alternate settings and their endpoints and compute the 
	// bandwidth they provide. As soon as we find a suitable setting
	// we return that. Apparently cameras usually order alt settings
	// with increasing bandwidth, so this algorithm should be good
	// enough.
	for (size_t alt = 1; alt <= interfaceptr->numAltsettings(); alt++) {
		ifdescptr = (*interfaceptr)[alt];
		EndpointDescriptorPtr	endpointptr = (*ifdescptr)[0];
		size_t	maxbandwidth = endpointptr->maxBandwidth();
		if (maxbandwidth > datarate) {
			debug(LOG_DEBUG, DEBUG_LOG, 0, "first alt setting "
				"matching data rate %.1fMBps: %d (%.1fMBps)",
				datarate / 1000000., alt,
				maxbandwidth / 1000000.);
			return alt;
		}
	}

	// if we get to this point, then we did not find a suitable alternate
	// setting, then there is no way we can satisfy the bandwidth
	// requirements of this camera. so we have to throw an exception
	throw USBError("no alternate setting with enough bandwidth found");
}
void	Device::releaseInterface(uint8_t interface) throw(USBError) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "releasing interface %d", interface);
	int	rc = libusb_release_interface(dev_handle, interface);
	rc = libusb_release_interface(dev_handle, interface);
	if (rc != LIBUSB_SUCCESS) {
		debug(LOG_ERR, DEBUG_LOG, 0, "cannot release interface %d: %s",
			interface, libusb_error_name(rc));
		throw USBError(libusb_error_name(rc));
	}
	debug(LOG_DEBUG, DEBUG_LOG, 0, "interface released");
}
DeviceDescriptorPtr	Device::descriptor() throw(USBError) {
	// get the device descriptor
	libusb_device_descriptor	d;
	int	rc = libusb_get_device_descriptor(dev, &d);
	if (rc != LIBUSB_SUCCESS) {
		throw USBError(libusb_error_name(rc));
	}

	// create a DeviceDescriptor object
	DeviceDescriptor	*devdesc = new DeviceDescriptor(*this, &d);
	return DeviceDescriptorPtr(devdesc);
}
/**
 * \brief Open the device.
 *
 * Most operations need that a USB device that is open. During a bus
 * scan, we may want to look at a device that is not open, so the default
 * is to just get a device, but not to open it. This method also opens
 * the device.
 */
void	Device::open() throw(USBError) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "open the device");
	// handle the case where the device has already been opened
	if (isOpen()) {
		return;
	}

	// the device is not open yet, so open it
	int	rc = libusb_open(dev, &dev_handle);
	if (rc != LIBUSB_SUCCESS) {
		throw USBError(libusb_error_name(rc));
	}
}
//================================================================================================
//
//   RestartControllerFromReset
//
//================================================================================================
//
IOReturn
AppleUSBOHCI::RestartControllerFromReset(void)
{
	UInt32		newValue;
	
	USBTrace( kUSBTOHCI, KTPOHCIRestartControllerFromReset, (uintptr_t)this, _uimInitialized, 0, 0);
	USBLog(3, "AppleUSBOHCI[%p]::RestartControllerFromReset - Re-loading UIM if necessary (%d)", this, _uimInitialized );
	
	// first, reinit the op regs
	InitializeOperationalRegisters();
	
    // Set OHCI to operational state and enable processing of control list.
	_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase);
	IOSync();
	IOSleep(3);			// wait the required 3 ms before turning on the lists
	
	// <rdar://problem/5981624> We need to make sure that the DRWE bit is properly set any time we go to the operational state
	newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
	if (!(newValue & kOHCIHcRhStatus_DRWE))
	{
		_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
		IOSync();
		
		if (_errataBits & kErrataNECIncompleteWrite)
		{
			UInt32		count = 0;
			newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);			// this bit SHOULD now be set
			while ((count++ < 10) && !(newValue & kOHCIHcRhStatus_DRWE))
			{
				USBError(1, "OHCI driver::RestartControllerFromReset - DRWE bit not sticking. Retrying.");
				_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
				IOSync();
				newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
			}
		}
	}
	
	_pOHCIRegisters->hcControl =  HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
												| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE) 
												| kOHCIHcControl_PLE | kOHCIHcControl_IE);
    IOSync();
	
    OHCIRootHubPower(1 /* kOn */);
	_myBusState = kUSBBusStateRunning;

	return kIOReturnSuccess;
}
//================================================================================================
//
// ResetControllerState
//
//================================================================================================
//
IOReturn
AppleUSBUHCI::ResetControllerState(void)
{
	UInt32				value;
	int					i;

	USBTrace( kUSBTUHCI, KTPUHCIResetControllerState, (uintptr_t)this, 0, 0, 0);
	USBLog(5, "AppleUSBUHCI[%p]::+ResetControllerState", this);

	// reset the controller
    Command(kUHCI_CMD_HCRESET);
    for(i=0; (i < kUHCI_RESET_DELAY) && (ioRead16(kUHCI_CMD) & kUHCI_CMD_HCRESET); i++) 
	{
        IOSleep(1);
    }
    if (i >= kUHCI_RESET_DELAY) 
	{
        USBError(1, "AppleUSBUHCI::ResetControllerStatecontroller - reset failed");
        return kIOReturnTimeout;
    }
    USBLog(5, "AppleUSBUHCI[%p]::ResetControllerStatecontroller - reset done after %d spins", this, i);

	// restore the frame list register
    if (_framesPaddr != NULL) 
	{
        ioWrite32(kUHCI_FRBASEADDR, _framesPaddr);
	}
	
	for (i = 0; i < kUHCI_NUM_PORTS; i++)
	{
		_lastPortStatus[i] = 0;
	}
	
	// Use 64-byte packets, and mark controller as configured
	Command(kUHCI_CMD_MAXP | kUHCI_CMD_CF);

    USBLog(5, "AppleUSBUHCI[%p]::-ResetControllerState", this);
	return kIOReturnSuccess;
}
Beispiel #18
0
IOReturn
IOUSBCommandPool::gatedReturnCommand(IOCommand * command)
{
	IOUSBCommand		*usbCommand		= OSDynamicCast(IOUSBCommand, command);					// only one of these should be non-null
	IOUSBIsocCommand	*isocCommand	= OSDynamicCast(IOUSBIsocCommand, command);

	USBLog(7,"IOUSBCommandPool[%p]::gatedReturnCommand %p", this, command);
	if (!command)
	{
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
		panic("IOUSBCommandPool::gatedReturnCommand( NULL )");
#endif
		return kIOReturnBadArgument;
	}
	
	if (command->fCommandChain.next &&
	    (&command->fCommandChain != command->fCommandChain.next || 
		 &command->fCommandChain != command->fCommandChain.prev))
	{
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
		kprintf("WARNING: gatedReturnCommand(%p) already on queue [next=%p prev=%p]\n", command, command->fCommandChain.next, command->fCommandChain.prev);
		panic("IOUSBCommandPool::gatedReturnCommand already on queue");
#endif
		char*		bt[8];
		
		OSBacktrace((void**)bt, 8);
		
		USBError(1,"IOUSBCommandPool::gatedReturnCommand  command already in queue, not putting it back into the queue, bt: [%p][%p][%p][%p][%p][%p][%p][%p]", bt[0], bt[1], bt[2], bt[3], bt[4], bt[5], bt[6], bt[7]);
		return kIOReturnBadArgument;
	}
	
	if (usbCommand)
	{
		IODMACommand *dmaCommand = usbCommand->GetDMACommand();
		if (dmaCommand)
		{
			if (dmaCommand->getMemoryDescriptor())
			{
				USBError(1, "IOUSBCommandPool::gatedReturnCommand - command (%p) still has dmaCommand(%p) with an active memory descriptor(%p)", usbCommand, dmaCommand, dmaCommand->getMemoryDescriptor());
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
				panic("IOUSBCommandPool::gatedReturnCommand -dmaCommand still has active IOMD");
#endif
			}
		}
		else
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - missing dmaCommand in IOUSBCommand");
		}
		
		// Test to poison the IOUSBCommand when returning it
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
		{
		char*					bt[kUSBCommandScratchBuffers];
	
		OSBacktrace((void**)bt, kUSBCommandScratchBuffers);
		for ( int i=0; i < kUSBCommandScratchBuffers; i++)
			usbCommand->SetBT(i, bt[i]);
		}
#endif
		// Clean up the command before returning it
		IOUSBCompletion			nullCompletion;
		nullCompletion.target = (void *) POISONVALUE;
		nullCompletion.action = (IOUSBCompletionAction) NULL;
		nullCompletion.parameter = (void *) POISONVALUE;
				
		usbCommand->SetSelector(INVALID_SELECTOR);
		usbCommand->SetRequest((IOUSBDeviceRequestPtr) POISONVALUE);
		usbCommand->SetAddress(0xFF);
		usbCommand->SetEndpoint(0xFF);
		usbCommand->SetDirection(0xFF);
		usbCommand->SetType(0xFF);
		usbCommand->SetBufferRounding(false);
		usbCommand->SetBuffer((IOMemoryDescriptor *) POISONVALUE);
		usbCommand->SetUSLCompletion(nullCompletion);
		usbCommand->SetClientCompletion(nullCompletion);
		usbCommand->SetDataRemaining(POISONVALUE);
		usbCommand->SetStage(0xFF);
		usbCommand->SetStatus(POISONVALUE);
		usbCommand->SetOrigBuffer((IOMemoryDescriptor *) POISONVALUE);
		usbCommand->SetDisjointCompletion(nullCompletion);
		usbCommand->SetDblBufLength(POISONVALUE);
		usbCommand->SetNoDataTimeout(POISONVALUE);
		usbCommand->SetCompletionTimeout(POISONVALUE);
		usbCommand->SetReqCount(POISONVALUE);
		usbCommand->SetMultiTransferTransaction(true);
		usbCommand->SetFinalTransferInTransaction(true);
		usbCommand->SetUseTimeStamp(true);
		usbCommand->SetIsSyncTransfer(FALSE);
		for ( int i=0; i < kUSBCommandScratchBuffers; i++)
			usbCommand->SetUIMScratch(i, POISONVALUE);
		usbCommand->SetStreamID(POISONVALUE);
		
		if ( usbCommand->GetBufferUSBCommand() != NULL )
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - GetBufferUSBCommand() is not NULL");
		}
		if ( usbCommand->GetRequestMemoryDescriptor() != NULL )
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - GetRequestMemoryDescriptor() is not NULL");
		}
		if ( usbCommand->GetBufferMemoryDescriptor() != NULL )
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - GetBufferMemoryDescriptor() is not NULL");
		}
		
		// Do not see these to anything but NULL as a lot of the code depends on checking for NULLness
		usbCommand->SetBufferUSBCommand(NULL);
		usbCommand->SetRequestMemoryDescriptor(NULL);
		usbCommand->SetBufferMemoryDescriptor(NULL);
	}
	
	if (isocCommand)
	{
		IODMACommand *dmaCommand = isocCommand->GetDMACommand();
		if (dmaCommand)
		{
			if (dmaCommand->getMemoryDescriptor())
			{
				USBError(1, "IOUSBCommandPool::gatedReturnCommand - isocCommand (%p) still has dmaCommand(%p) with an active memory descriptor(%p)", isocCommand, dmaCommand, dmaCommand->getMemoryDescriptor());
#if DEBUG_LEVEL != DEBUG_LEVEL_PRODUCTION
				panic("IOUSBCommandPool::gatedReturnCommand - dmaCommand still has active IOMD (isoc)");
#endif
			}
		}
		else
		{
			USBError(1,"IOUSBCommandPool::gatedReturnCommand - missing dmaCommand in IOUSBIsocCommand");
		}
	}
	return IOCommandPool::gatedReturnCommand(command);
}
static void 
DisjointCompletion(IOUSBController *me, IOUSBCommand *command, IOReturn status, UInt32 bufferSizeRemaining)
{
    IOBufferMemoryDescriptor	*buf = NULL;
	IODMACommand				*dmaCommand = NULL;

	USBTrace_Start( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)me, (uintptr_t)command, status, bufferSizeRemaining );
	
    if (!me || !command)
    {
		USBError(1, "DisjointCompletion sanity check failed - me(%p) command (%p)", me, command);
		return;
    }
	
	buf = OSDynamicCast(IOBufferMemoryDescriptor, command->GetBuffer());
	dmaCommand = command->GetDMACommand();
	
	if (!dmaCommand || !buf)
	{
		USBLog(1, "%s[%p]::DisjointCompletion - no dmaCommand, or buf(%p) is not an IOBMD", me->getName(), me, command->GetBuffer());
		USBTrace( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)me, (uintptr_t)command->GetBuffer(), 0, 1 );
		return;
	}
	
	if (dmaCommand->getMemoryDescriptor())
	{
		if (dmaCommand->getMemoryDescriptor() != buf)
		{
			USBLog(1, "%s[%p]::DisjointCompletion - buf(%p) doesn't match getMemoryDescriptor(%p)", me->getName(), me, buf, dmaCommand->getMemoryDescriptor());
			USBTrace( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)me, (uintptr_t)buf, (uintptr_t)dmaCommand->getMemoryDescriptor(), 2 );
		}
		
		// need to complete the dma command
		USBLog(6, "%s[%p]::DisjointCompletion - clearing memory descriptor (%p) from dmaCommand (%p)", me->getName(), me, dmaCommand->getMemoryDescriptor(), dmaCommand);
		dmaCommand->clearMemoryDescriptor();
	}
	
    if (command->GetDirection() == kUSBIn)
    {
		USBLog(5, "%s[%p]::DisjointCompletion, copying %d out of %d bytes to desc %p from buffer %p", me->getName(), me, (int)(command->GetDblBufLength()-bufferSizeRemaining), (int)command->GetDblBufLength(), command->GetOrigBuffer(), buf);
		command->GetOrigBuffer()->writeBytes(0, buf->getBytesNoCopy(), (command->GetDblBufLength()-bufferSizeRemaining));
    }
	
    buf->complete();
	buf->release();								// done with this buffer
	command->SetBuffer(NULL);
	
    // now call through to the original completion routine
    IOUSBCompletion completion = command->GetDisjointCompletion();
	
	if ( !command->GetIsSyncTransfer() )
	{
		// Free our command now that we have the completion and we are not going to use it anymore
		me->ReturnUSBCommand(command);
	}
	
   	if (completion.action)
	{
		USBLog(status == kIOReturnSuccess ? 7 : 3, "%s[%p]::DisjointCompletion calling through to %p - status 0x%x!", me->getName(), me, completion.action, (uint32_t)status);
		(*completion.action)(completion.target, completion.parameter, status, bufferSizeRemaining);
	}
	
	USBTrace_End( kUSBTController, kTPControllerDisjointCompletion, (uintptr_t)completion.target, (uintptr_t)completion.parameter, status, bufferSizeRemaining);
}
//================================================================================================
//
//   SuspendUSBBus
//
//================================================================================================
//
void
AppleUSBOHCI::SuspendUSBBus(bool goingToSleep)
{
    UInt32			something;
    UInt32			hcControl;
	
	USBTrace( kUSBTOHCI, KTPOHCISuspendUSBBus, (uintptr_t)this, goingToSleep, 0, 0);
	USBLog(5,"AppleUSBOHCI[%p]::SuspendUSBBus goingToSleep = %s", this, goingToSleep ? "TRUE" : "FALSE");
	
    // 1st turn off all list processing
    //
    hcControl = USBToHostLong(_pOHCIRegisters->hcControl);
    hcControl &= ~(kOHCIHcControl_CLE | kOHCIHcControl_BLE | kOHCIHcControl_PLE | kOHCIHcControl_IE);
	
    _pOHCIRegisters->hcControl = HostToUSBLong(hcControl);
    
    // We used to wait for a SOF interrupt here.  Now just sleep for 1 ms.
    //
    IOSleep(1);
    
    // check for the WDH register to see if we need to process is [2405732]
    //
    if ( _writeDoneHeadInterrupt )
    {
        USBError(1,"AppleUSBOHCI[%p]::SuspendUSBBus Processing WDH before suspending", this);
        PollInterrupts();
    }
    
	if ( goingToSleep )
	{
		// now tell the controller to put the bus into suspend mode
		if (_errataBits & kErrataOHCINoGlobalSuspendOnSleep)
		{
			UInt32			port;
			hcControl = kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase;
			for (port=0; port < _rootHubNumPorts; port++)
			{
				_savedHcRhPortStatus[port] = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
				USBLog(5, "AppleUSBOHCI[%p]::SuspendUSBBus - port %d _savedHcRhPortStatus(%p)", this, (int)port+1, (void*)_savedHcRhPortStatus[port]);
			}
		}
		else
		{
			hcControl = kOHCIFunctionalState_Suspend << kOHCIHcControl_HCFSPhase;
		}
		
		if (_hasPCIPwrMgmt)
			hcControl |= kOHCIHcControl_RWC | kOHCIHcControl_RWE;
		
		_pOHCIRegisters->hcControl = HostToUSBLong(hcControl);
		IOSleep(3);	// wait 3 milliseconds for things to settle
    }
	else
	{
		UInt32			port;
		for (port=0; port < _rootHubNumPorts; port++)
		{
			USBLog(7, "AppleUSBOHCI[%p]::SuspendUSBBus - hcRhPortStatus[%d] = %p", this, (int)port+1, (void*) USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]));
		}
	}
}
/**
 * \brief Construct a camera from a USB Device
 *
 * The constructor undertakes an extensive analysis of the descriptors
 * to find the video control and video streaming interfaces of the video
 * function of the device. It also makes sure no kernel driver is attached
 * to the device. It does not, however, claim any of the interfaces, this
 * is done when the device is really used.
 * \param _device	an USB device to open as a UVC camera
 * \param force		force opening as camera even if the
 *			interface associaten descriptor does not
 *			declare itself as a video interface association
 *                      descriptor (handles the TIS camera)
 * XXX apparently the force parameter is never used, so the question should be
 *     asked whether we can remove it.
 */
UVCCamera::UVCCamera(Device& _device, bool /* force */) throw(USBError)
	: device(_device) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "create a UVC camera object");

	// make sure the camera is open, this most probably will not have
	// any effect
	device.open();

	// scan the active configuration for one that has an Interface
	// association descriptor
	ConfigurationPtr config = device.activeConfig();
	if (config->extra().size() == 0) {
		debug(LOG_ERR, DEBUG_LOG, 0, "no extra descriptors");
		throw USBError("no InterfaceAssociationDescriptor");
	}

	// get the list of interface association descriptors
	std::list<USBDescriptorPtr>	iadlist
		= device.interfaceAssociationDescriptors(true);
	if (0 == iadlist.size()) {
		throw USBError("no Video Interface Association found");
	}
	iadptr = *iadlist.begin();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "Video Interface Association found");

	// get the control interface, and the list of interface descriptors
	// for the control interface, and claim it
	uint8_t	ci = controlInterfaceNumber();
	videocontrol = (*config)[ci];
	debug(LOG_DEBUG, DEBUG_LOG, 0, "Control interface number: %d", ci);
	videocontrol->detachKernelDriver();

	// we also need to know all the video control descriptors appended
	// to this InterfaceDescriptor. The VideoControlDescriptorFactory
	// does that.
	debug(LOG_DEBUG, DEBUG_LOG, 0, "parse the video control descriptors");
	InterfaceDescriptorPtr	controlinterface = (*videocontrol)[0];
	VideoControlDescriptorFactory	vcdf(device);
	videocontroldescriptors = vcdf.descriptors(controlinterface->extra());
	std::cout << videocontroldescriptors[0];
	
	// now claim get the various interface descriptors, i.e. the
	// alternate settings for an interface
	int	interfacecount = iad().bInterfaceCount();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "interfaces in association: %d",
		interfacecount);

	// now parse the video streaming interfaces
	debug(LOG_DEBUG, DEBUG_LOG, 0, "parse streaming interface descriptors");
	VideoStreamingDescriptorFactory	vsf(device);
	for (int vsif = controlInterfaceNumber() + 1;
		vsif < controlInterfaceNumber() + iad().bInterfaceCount();
		vsif++) {
		debug(LOG_DEBUG, DEBUG_LOG, 0,
			"analyzing video streaming interface %d", vsif);
		InterfacePtr	interface = (*config)[vsif];
		// only alternate setting 0 contains the formats
		InterfaceDescriptorPtr	id = (*interface)[0];
		std::string	extra = id->extra();
		debug(LOG_DEBUG, DEBUG_LOG, 0, "extra descriptors: %d bytes",
			extra.size());
		USBDescriptorPtr	vsd = vsf.descriptor(extra);
		debug(LOG_DEBUG, DEBUG_LOG, 0, "parse complete");
		videostreaming.push_back(vsd);
	}
	debug(LOG_DEBUG, DEBUG_LOG, 0, "UVCCamera constructed");
}
IOReturn
AppleUSBUHCI::RHSuspendPort(int port, bool suspended)
{
    UInt16 cmd, value;
    
    USBLog(3, "AppleUSBUHCI[%p]::RHSuspendPort %d (%s) _rhPortBeingResumed[%d](%s)", this, port, suspended ? "SUSPEND" : "RESUME", (int)port-1, _rhPortBeingResumed[port-1] ? "true" : "false");
	showRegisters(7, "RHSuspendPort");
    port--; // convert 1-based to 0-based.

	if (_rhPortBeingResumed[port])
	{
		if (!suspended)
		{
			USBLog(3, "AppleUSBUHCI[%p]::RHSuspendPort - resume on port (%d) already being resumed - gracefully ignoring", this, (int)port+1);
			return kIOReturnSuccess;
		}
		USBLog(1, "AppleUSBUHCI[%p]::RHSuspendPort - trying to suspend port (%d) which is being resumed - UNEXPECTED", this, (int)port+1);
		USBTrace( kUSBTUHCI, kTPUHCIRHSuspendPort, (uintptr_t)this, (int)port+1, 0, 0);
	}
	
    cmd = ioRead16(kUHCI_CMD);
    value = ReadPortStatus(port) & kUHCI_PORTSC_MASK;
    
    if (suspended) 
	{
        value |= kUHCI_PORTSC_SUSPEND;
        value &= ~kUHCI_PORTSC_RD;
    } else 
	{
        if (cmd & kUHCI_CMD_EGSM) 
		{
            /* Can't un-suspend a port during global suspend. */
            USBError(1, "AppleUSBUHCI[%p]: attempt to resume during global suspend", this);
            return kIOReturnError;
        }
        value |= (kUHCI_PORTSC_SUSPEND | kUHCI_PORTSC_RD);
    }
    // Always enable the port also.
    value |= kUHCI_PORTSC_PED;

    USBLog(5, "AppleUSBUHCI[%p]: writing (%p) to port control", this, (void*)value);
    
    WritePortStatus(port, value);
    
    if (suspended) 
	{
        /* Suspending.
         * Sleep for 3ms to ensure nothing goes out on the bus
         * until devices are suspended.
         */
        IOSleep(3);
        
    } else 
	{
        // Resuming
		USBLog(5,"AppleUSBUHCI[%p]::RHSuspendPort - resuming port %d, calling out to timer", this, (int)port+1);
		_rhPortBeingResumed[port] = true;
		thread_call_enter1(_rhResumePortTimerThread[port], (void*)(port+1));
    }

    USBLog(5, "AppleUSBUHCI[%p]::RHSuspendPort %d (%s) calling UIMRootHubStatusChange", this, port+1, suspended ? "SUSPEND" : "RESUME");

    UIMRootHubStatusChange();        
    
    USBLog(5, "AppleUSBUHCI[%p]::RHSuspendPort %d (%s) DONE", this, port+1, suspended ? "SUSPEND" : "RESUME");

    return kIOReturnSuccess;
}
//================================================================================================
//
//   ResumeUSBBus
//
//================================================================================================
//
void
AppleUSBOHCI::ResumeUSBBus(bool wakingFromSleep)
{
	UInt32		newValue;
	
	USBTrace( kUSBTOHCI, KTPOHCIResumeUSBBus, (uintptr_t)this, wakingFromSleep, 0, 0);
	USBLog(5,"AppleUSBOHCI[%p]::ResumeUSBBus wakingFromSleep = %s", this, wakingFromSleep ? "TRUE" : "FALSE" );
	
    switch ((USBToHostLong(_pOHCIRegisters->hcControl) & kOHCIHcControl_HCFS) >> kOHCIHcControl_HCFSPhase )
    {
        case kOHCIFunctionalState_Suspend:
			// Place the USB bus into the resume State
			USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - Resuming bus from Suspend state", this);
			_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Resume << kOHCIHcControl_HCFSPhase);
			// intentional fall through
        case kOHCIFunctionalState_Resume:
			// Complete the resume by waiting for the required delay
			if (_errataBits & kErrataLucentSuspendResume)
                // JRH 08-27-99
                // this is a very simple yet clever hack for working around a bug in the Lucent controller
                // By using 35 instead of 20, we overflow an internal 5 bit counter by exactly 3ms, which 
                // stops an errant 3ms suspend from appearing on the bus
			{
				USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus- Delaying 35 milliseconds in resume state", this);
				IOSleep(35);
			}
			else
			{
				USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - Delaying 20 milliseconds in resume state", this);
				IOSleep(20);
			}
			// intentional fall through
        case kOHCIFunctionalState_Reset:
			// Place the USB bus into the operational State
			USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - Changing bus to operational", this);
			_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase);
			IOSync();
			IOSleep(3);			// wait the required 3 ms before turning on the lists
			
			// <rdar://problem/5981624> We need to make sure that the DRWE bit is properly set any time we go to the operational state
			newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
			if (!(newValue & kOHCIHcRhStatus_DRWE))
			{
				_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
				IOSync();
				
				if (_errataBits & kErrataNECIncompleteWrite)
				{
					UInt32		count = 0;
					newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);			// this bit SHOULD now be set
					while ((count++ < 10) && !(newValue & kOHCIHcRhStatus_DRWE))
					{
						USBError(1, "OHCI driver::ResumeUSBBus - DRWE bit not sticking. Retrying.");
						_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
						IOSync();
						newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
					}
				}
			}
			_pOHCIRegisters->hcControl =  HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
														| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE) 
														| kOHCIHcControl_PLE | kOHCIHcControl_IE);
			IOSync();
			break;
			
        default:
            USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus  Bus already operational - turning on the lists", this);
			_pOHCIRegisters->hcControl =  HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
														| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE) 
														| kOHCIHcControl_PLE | kOHCIHcControl_IE);
			IOSync();
			
            break;
    }
	
	// Do this after waking the controller so you see wakeups.
	if (wakingFromSleep)
	{
		UInt32		port, portSC;
		
		IOSleep(1);
		for (port=0; port < _rootHubNumPorts; port++)
		{
			UInt32	portSC = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
			//USBLog(6, "AppleUSBOHCI[%p]::ResumeUSBBus Port %d, portSC(%p)", this, (int)port+1, (void*)portSC);
			if (portSC & kOHCIHcRhPortStatus_CSC)
			{
				if (portSC & kOHCIHcRhPortStatus_PES)
				{
					USBError(1, "USB (OHCI):Port %d on bus 0x%x has connect status change but is still enabled. setting clear port enable. hcRhPortStatus(%p)", (int)port+1, (uint32_t)_busNumber, (void*)portSC);
					_pOHCIRegisters->hcRhPortStatus[port] = HostToUSBLong(kOHCIHcRhPortStatus_CCS);				// CCS when writing is CPE
					IOSleep(1);
					portSC = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
				}
				else
				{
					USBLog(5, "AppleUSBOHCI[%p]::ResumeUSBBus Port %d on bus 0x%x connected or disconnected. portSC(%p)", this, (int)port+1, (uint32_t)_busNumber, (void*)portSC);
					// IOLog("USB (OHCI):Port %d on bus 0x%x connected or disconnected. portSC(%p)\n", (int)port+1, (uint32_t)_busNumber, (void*)portSC);
				}
			}
			else if (portSC & kOHCIHcRhPortStatus_PSSC)
			{
				if (_rootHubDevice && _rootHubDevice->GetPolicyMaker())
				{
					// Make sure to send port index, not port number
					_rootHubDevice->GetPolicyMaker()->message(kIOUSBMessageRootHubWakeEvent, this, (void *)(uintptr_t) (port));
				}
				else
				{
					IOLog("USB (OHCI):Port %d on bus 0x%x has remote wakeup from some device\n", (int)port+1, (uint32_t)_busNumber);
				}
				USBLog(5, "AppleUSBOHCI[%p]::ResumeUSBBus Port %d on bus 0x%x has remote wakeup from some device", this, (int)port+1, (uint32_t)_busNumber);
			}
			else if ((_errataBits & kErrataOHCINoGlobalSuspendOnSleep)					// if we are on these controllers
					 && (portSC & kOHCIHcRhPortStatus_CCS)								// and we are currently connected
					 && !(portSC & kOHCIHcRhPortStatus_PES)								// and we are not currently enabled
					 &&  (_savedHcRhPortStatus[port] & kOHCIHcRhPortStatus_PES))		// and we were enabled before we went to sleep
			{
				USBError(1, "USB (OHCI):Port %d on bus 0x%x is connected but not enabled. trying to set port enable. hcRhPortStatus(%p) _savedHcRhPortStatus(%p)", (int)port+1, (uint32_t)_busNumber, (void*)portSC, (void*)_savedHcRhPortStatus[port]);
				_pOHCIRegisters->hcRhPortStatus[port] = HostToUSBLong(kOHCIHcRhPortStatus_PES);				// CCS when writing is CPE
				IOSleep(1);
				portSC = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
				USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - new hcRhPortStatus(%p)", this, (void*)portSC);
			}
			_savedHcRhPortStatus[port] = 0;												// clear this out to be safe once we have no more need for it
		}
	}
}
/**
 * \brief Select format and frame.
 *
 * This method negotiates format and frame with the device. This also
 * implies a frame interval setting, and the bandwith depends on this
 * setting as well. However, selecting the format and frame a priori 
 * does not yet fix the bandwidth, this is a consideration only for
 * isochronous transfers. Therefore this method does not do any bandwidth
 * negotiation, but leaves this to the getFrame method.
 *
 * \param interface	video streaming interface number, not the index in
 *			the videostreaming array.
 * \param format	format number, one larger than the format index
 * \param frame		frame number, equal to the bFrameIndex field of the
 *			frame descriptor
 */
void	UVCCamera::selectFormatAndFrame(uint8_t interface,
		uint8_t format, uint8_t frame) throw(USBError) {
	debug(LOG_DEBUG, DEBUG_LOG, 0,
		"select interface %d, format %d, frame %d",
		interface, format, frame);
	// We want to negotiate use of the a given format and frame.
	// To do this, we send a SET probe
	vs_control_request_t	control_request;
	memset(&control_request, 0, sizeof(control_request));
	control_request.bFormatIndex = format;
	control_request.bFrameIndex = frame;
	control_request.dwFrameInterval
		= minFrameInterval(interface, format, frame);

	// do we have to claim the interface?
	InterfacePtr	interfaceptr = (*device.activeConfig())[interface];
	debug(LOG_DEBUG, DEBUG_LOG, 0, "interface %d with %d alt settings",
		interfaceptr->interfaceNumber(),
		interfaceptr->numAltsettings());
#if 1
	interfaceptr->claim();
#endif
	VideoStreamingProbeControlRequest	rset(interfaceptr, SET_CUR,
							&control_request);
	device.controlRequest(&rset);

	// now probe the same thing, this should return a recommended
	// setting 
	VideoStreamingProbeControlRequest	rget(interfaceptr, GET_CUR);
	device.controlRequest(&rget);
	if (rget.data()->bFormatIndex != format) {
		throw USBError("cannot negotiate format index");
	}
	if (rget.data()->bFrameIndex != frame) {
		throw USBError("cannot negotiate frame index");
	}

	// if we get to this point, then negotiating the format and frame
	// was successful, and we can commit the negotiated paramters
	VideoStreamingCommitControlRequest	rcommit(interfaceptr, SET_CUR,
							rget.data());
	device.controlRequest(&rcommit);

	// we now also have to find out how many bits per pixel we can
	// expect
	USBDescriptorPtr	formatptr
		= getFormatDescriptor(interface, format);
	FormatFrameBasedDescriptor	*fd
		= dynamic_cast<FormatFrameBasedDescriptor *>(&*formatptr);
	if (NULL == fd) {
		debug(LOG_DEBUG, DEBUG_LOG, 0, "unknown pixel size");
		bitsPerPixel = 1;
	} else {
		bitsPerPixel = fd->bBitsPerPixel();
		debug(LOG_DEBUG, DEBUG_LOG, 0, "bits per pixel: %d",
			bitsPerPixel);
	}

	// just to be on the safe side, we should ask again what the
	// current settings are
	getCur(interface);
}
IOReturn 
IOUSBController::IsocIO(IOMemoryDescriptor *			buffer,
						UInt64							frameStart,
						UInt32							numFrames,
						IOUSBLowLatencyIsocFrame *		frameList,
						USBDeviceAddress				address,
						Endpoint *						endpoint,
						IOUSBLowLatencyIsocCompletion * completion,
						UInt32							updateFrequency)
{
    IOReturn				err = kIOReturnSuccess;
    IOUSBIsocCommand *		command = NULL;
    bool					crossEndianRequest = false;
	IODMACommand *			dmaCommand = NULL;
	bool					syncTransfer = false;
    
	// Validate the completion
	//
	USBLog(7, "%s[%p]::IsocIO(LL)", getName(), this);
	if (completion == 0)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) - No completion.  Returning kIOReturnNoCompletion(0x%x)", getName(), this, kIOReturnNoCompletion);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnNoCompletion, 0, 3 );		
		return kIOReturnNoCompletion;
	}
	
	// Validate the commandGate
	//
	if (_commandGate == 0)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) - Could not get _commandGate.  Returning kIOReturnInternalError(0x%x)", getName(), this, kIOReturnInternalError);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnInternalError, 0, 4 );
		return kIOReturnInternalError;
	}
	
	// If the high order bit of the endpoint transfer type is set, then this means it's a request from an Rosetta client
	if ( endpoint->direction & 0x80 )
	{
		endpoint->direction &= ~0x80;
		crossEndianRequest = true;
	}
	
	// Validate the direction of the endpoint -- it has to be kUSBIn or kUSBOut
	if ( (endpoint->direction != kUSBOut) && ( endpoint->direction != kUSBIn) )
	{		
		USBLog(1, "%s[%p]::IsocIO(LL) - Direction is not kUSBOut or kUSBIn (%d).  Returning kIOReturnBadArgument(0x%x)", getName(), this, endpoint->direction, kIOReturnBadArgument);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, endpoint->direction, kIOReturnBadArgument, 6 );
		return kIOReturnBadArgument;
	}
	
    if ( (uintptr_t)completion->action == (uintptr_t)&IOUSBSyncIsoCompletion )
	{
		syncTransfer = true;
        if ( _workLoop->onThread() )
        {
            USBError(1,"IOUSBController(%s)[%p]::DoIsocTransfer sync request on workloop thread.  Use async!", getName(), this);
            return kIOUSBSyncRequestOnWLThread;
        }
		
	}
	
	command = (IOUSBIsocCommand *)_freeUSBIsocCommandPool->getCommand(false);
    // If we couldn't get a command, increase the allocation and try again
    //
    if ( command == NULL )
    {
        IncreaseIsocCommandPool();
        
        command = (IOUSBIsocCommand *)_freeUSBIsocCommandPool->getCommand(false);
        if ( command == NULL )
        {
            USBLog(1, "%s[%p]::IsocIO(LL) Could not get a IOUSBIsocCommand", getName(), this);
			USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnNoResources, 0, 5 );
            return kIOReturnNoResources;
        }
    }

	dmaCommand = command->GetDMACommand();
	if (!dmaCommand)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) no IODMACommand in the IOUSBCommand", getName(), this);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, kIOReturnNoResources, 0, 1 );		
		return kIOReturnNoResources;
	}
	
	USBLog(7, "%s[%p]::IsocIO(LL) - putting buffer %p into dmaCommand %p which has getMemoryDescriptor %p", getName(), this, buffer, command->GetDMACommand(), command->GetDMACommand()->getMemoryDescriptor());
	err = dmaCommand->setMemoryDescriptor(buffer);								// this automatically calls prepare()
	if (err)
	{
		USBLog(1, "%s[%p]::IsocIO(LL) - dmaCommand[%p]->setMemoryDescriptor(%p) failed with status (%p)", getName(), this, command->GetDMACommand(), buffer, (void*)err);
		USBTrace( kUSBTController, kTPIsocIOLL, (uintptr_t)this, err, 0, 2 );		
		_freeUSBIsocCommandPool->returnCommand(command);
		return err;
	}
	
	// If the high order bit of the endpoint transfer type is set, then this means it's a request from an Rosetta client
	command->SetRosettaClient(crossEndianRequest);
	
	command->SetIsSyncTransfer(syncTransfer);
	
	// Setup the direction
	if (endpoint->direction == kUSBOut) 
	{
		command->SetSelector(WRITE);
		command->SetDirection(kUSBOut);
	}
	else if (endpoint->direction == kUSBIn) 
	{
		command->SetSelector(READ);
		command->SetDirection(kUSBIn);
	}

	command->SetUseTimeStamp(false);
	command->SetAddress(address);
	command->SetEndpoint(endpoint->number);
	command->SetBuffer(buffer);
	command->SetCompletion( * ((IOUSBIsocCompletion *) completion) );
	command->SetStartFrame(frameStart);
	command->SetNumFrames(numFrames);
	command->SetFrameList( (IOUSBIsocFrame *) frameList);
	command->SetStatus(kIOReturnBadArgument);
	command->SetUpdateFrequency(updateFrequency);
	command->SetLowLatency(true);

	err = _commandGate->runAction(DoIsocTransfer, command);
	
	// If we have a sync request, then we always return the command after the DoIsocTransfer.  If it's an async request, we only return it if 
	// we get an immediate error
	//
	if ( syncTransfer || (kIOReturnSuccess != err) )
	{
		IODMACommand		*dmaCommand = command->GetDMACommand();
		IOMemoryDescriptor	*memDesc = dmaCommand ? (IOMemoryDescriptor	*)dmaCommand->getMemoryDescriptor() : NULL;
		
		if (memDesc)
		{
			USBLog(7, "%s[%p]::IsocIO(LL) - sync xfer or err return - clearing memory descriptor (%p) from dmaCommand (%p)", getName(), this, memDesc, dmaCommand);
			dmaCommand->clearMemoryDescriptor();
		}
		_freeUSBIsocCommandPool->returnCommand(command);
	}

    return err;
}
IOReturn 
IOUSBController::Write(IOMemoryDescriptor *buffer, USBDeviceAddress address, Endpoint *endpoint, IOUSBCompletion *completion, UInt32 noDataTimeout, UInt32 completionTimeout, IOByteCount reqCount)
{
    IOReturn				err = kIOReturnSuccess;
    IOUSBCommand *			command = NULL;
	IODMACommand *			dmaCommand = NULL;
    IOUSBCompletion			nullCompletion;
    int						i;
	bool					isSyncTransfer = false;
	
    USBLog(7, "%s[%p]::Write - reqCount = %qd", getName(), this, (uint64_t)reqCount);
    
    // Validate its a outty pipe and that we have a buffer
    if ((endpoint->direction != kUSBOut) || !buffer || (buffer->getLength() < reqCount))
    {
        USBLog(5, "%s[%p]::Write - direction is not kUSBOut (%d), No Buffer, or buffer length < reqCount (%qd < %qd). Returning kIOReturnBadArgument(0x%x)", getName(), this, endpoint->direction,  (uint64_t)buffer->getLength(), (uint64_t)reqCount, kIOReturnBadArgument);
		return kIOReturnBadArgument;
    }

    if ((endpoint->transferType != kUSBBulk) && (noDataTimeout || completionTimeout))
    {
        USBLog(5, "%s[%p]::Write - Pipe is NOT kUSBBulk (%d) AND specified a timeout (%d, %d).  Returning kIOReturnBadArgument(0x%x)", getName(), this, endpoint->transferType, (uint32_t)noDataTimeout, (uint32_t)completionTimeout, kIOReturnBadArgument);
		return kIOReturnBadArgument;							// timeouts only on bulk pipes
    }
	
    // Validate the command gate
    if (!_commandGate)
    {
        USBLog(5, "%s[%p]::Write - Could not get _commandGate.  Returning kIOReturnInternalError(0x%x)", getName(), this, kIOReturnInternalError);
		return kIOReturnInternalError;
    }

    if (  (uintptr_t) completion->action == (uintptr_t) &IOUSBSyncCompletion )
	{
		isSyncTransfer = true;
		// 7889995 - check to see if we are on the workloop thread before setting up the IOUSBCommand
		if ( _workLoop->onThread() )
		{
            USBError(1,"IOUSBController(%s)[%p]::Write sync request on workloop thread.  Use async!", getName(), this);
            return kIOUSBSyncRequestOnWLThread;
		}
	}
	
	
    // allocate the command
    command = (IOUSBCommand *)_freeUSBCommandPool->getCommand(false);
    
    // If we couldn't get a command, increase the allocation and try again
    //
    if ( command == NULL )
    {
        IncreaseCommandPool();
        
        command = (IOUSBCommand *)_freeUSBCommandPool->getCommand(false);
        if ( command == NULL )
        {
            USBLog(3,"%s[%p]::Write Could not get a IOUSBCommand",getName(),this);
            return kIOReturnNoResources;
        }
    }

	// 7455477: from this point forward, we have the command object, and we need to be careful to put it back if there is an error..
	if (reqCount)
	{
		IOMemoryDescriptor	*memDesc;
		
		dmaCommand = command->GetDMACommand();
		
		if (!dmaCommand)
		{
			USBLog(1, "%s[%p]::Write - no DMA COMMAND", getName(), this);
			USBTrace( kUSBTController, kTPControllerWrite, (uintptr_t)this, kIOReturnNoResources, 0, 1 );
            err = kIOReturnNoResources;
		}
		else
		{
			memDesc = (IOMemoryDescriptor *)dmaCommand->getMemoryDescriptor();
			if (memDesc)
			{
				USBLog(1, "%s[%p]::Write - dmaCommand (%p) already contains memory descriptor (%p) - clearing", getName(), this, dmaCommand, memDesc);
				USBTrace( kUSBTController, kTPControllerWrite, (uintptr_t)this, (uintptr_t)dmaCommand, (uintptr_t)memDesc, 2 );
				dmaCommand->clearMemoryDescriptor();
			}
			USBLog(7, "%s[%p]::Write - setting memory descriptor (%p) into dmaCommand (%p)", getName(), this, buffer, dmaCommand);
			err = dmaCommand->setMemoryDescriptor(buffer);
			if (err)
			{
				USBTrace( kUSBTController, kTPControllerWrite, (uintptr_t)this, err, 0, 4 );
				USBLog(1, "%s[%p]::Write - err(%p) attempting to set the memory descriptor to the dmaCommand", getName(), this, (void*)err);
			}
		}

	}
	
	if (!err)
	{
		command->SetIsSyncTransfer(isSyncTransfer);
		command->SetUseTimeStamp(false);
		command->SetSelector(WRITE);
		command->SetRequest(0);            // Not a device request
		command->SetAddress(address);
		command->SetEndpoint(endpoint->number);
#ifdef SUPPORTS_SS_USB
    	command->SetStreamID(0);
#endif
		command->SetDirection(kUSBOut);
		command->SetType(endpoint->transferType);
		command->SetBuffer(buffer);
		command->SetReqCount(reqCount);
		command->SetClientCompletion(*completion);
		command->SetNoDataTimeout(noDataTimeout); 
		command->SetCompletionTimeout(completionTimeout);
		command->SetMultiTransferTransaction(false);
		command->SetFinalTransferInTransaction(false);
		for (i=0; i < 10; i++)
			command->SetUIMScratch(i, 0);

		nullCompletion.target = (void *) NULL;
		nullCompletion.action = (IOUSBCompletionAction) NULL;
		nullCompletion.parameter = (void *) NULL;
		command->SetDisjointCompletion(nullCompletion);

		err = CheckForDisjointDescriptor(command, endpoint->maxPacketSize);
		if (!err)
		{			
			err = _commandGate->runAction(DoIOTransfer, command);
		}
	}
	
	// 7455477: handle and and all errors which may have occured above
	// If we have a sync request, then we always return the command after the DoIOTransfer.  If it's an async request, we only return it if 
	// we get an immediate error
	//
	if ( isSyncTransfer || (kIOReturnSuccess != err) )
	{
		IOMemoryDescriptor	*memDesc = dmaCommand ? (IOMemoryDescriptor	*)dmaCommand->getMemoryDescriptor() : NULL;

		if (!isSyncTransfer)
		{
			USBLog(2, "%s[%p]::Write - General error (%p) - cleaning up - command(%p) dmaCommand(%p)", getName(), this, (void*)err, command, dmaCommand);
		}

		if (memDesc)
		{
			USBLog(7, "%s[%p]::Write - General error (%p) - clearing memory descriptor (%p) from dmaCommand (%p)", getName(), this, (void*)err, memDesc, dmaCommand);
			dmaCommand->clearMemoryDescriptor();
		}
		nullCompletion = command->GetDisjointCompletion();
		if (nullCompletion.action)
		{
			USBLog(2, "%s[%p]::Write - SYNC xfer or immediate error with Disjoint Completion", getName(), this);
		}
		_freeUSBCommandPool->returnCommand(command);
	}
	
    return err;
}
//================================================================================================
//
//  MergeDictionaryIntoProvider
//
//  We will iterate through the dictionary that we want to merge into our provider.  If
//  the dictionary entry is not an OSDictionary, we will set that property into our provider.  If it is a
//  OSDictionary, we will get our provider's entry and merge our entry into it, recursively.
//
//================================================================================================
//
bool
IOUSBUserClientInit::MergeDictionaryIntoProvider(IOService * provider, OSDictionary * dictionaryToMerge)
{
    const OSSymbol * 		dictionaryEntry = NULL;
    OSCollectionIterator * 	iter = NULL;
    bool			result = false;

    USBLog(6,"+%s[%p]::MergeDictionary(%p)IntoProvider(%p)", getName(), this, dictionaryToMerge, provider);

    if (!provider || !dictionaryToMerge)
        return false;

    // Get the dictionary whose entries we need to merge into our provider and get
    // an iterator to it.
    //
    iter = OSCollectionIterator::withCollection((OSDictionary *)dictionaryToMerge);
    if ( iter != NULL )
    {
        // Iterate through the dictionary until we run out of entries
        //
        while ( NULL != (dictionaryEntry = (const OSSymbol *)iter->getNextObject()) )
        {
            const char *	str = NULL;
            OSDictionary *	sourceDictionary = NULL;
            OSDictionary *	providerDictionary = NULL;
            OSObject *		providerProperty = NULL;

            // Get the symbol name for debugging
            //
            str = dictionaryEntry->getCStringNoCopy();
            USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  merging \"%s\"", getName(), this, str);

            // Check to see if our destination already has the same entry.
            //
            providerProperty = provider->getProperty(dictionaryEntry);
            if ( providerProperty )
            {
                USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  provider already had property %s", getName(), this, str);
                providerDictionary = OSDynamicCast(OSDictionary, providerProperty);
                if ( providerDictionary )
                {
                    USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  provider's %s is also a dictionary (%p)", getName(), this, str, providerDictionary);
                }
            }
            
            // See if our source entry is also a dictionary
            //
            sourceDictionary = OSDynamicCast(OSDictionary, dictionaryToMerge->getObject(dictionaryEntry));
            if ( sourceDictionary )
            {
                USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  source dictionary had %s as a dictionary (%p)", getName(), this, str, sourceDictionary);
            }
            
            if ( providerDictionary &&  sourceDictionary )
            {
                // Need to merge our entry into the provider's dictionary.  However, we don't have a copy of our dictionary, just
                // a reference to it.  So, we need to make a copy of our provider's dictionary so that we don't modify our provider's
                // dictionary using non-synchronize calls.
                //
                OSDictionary *		localCopyOfProvidersDictionary;
                UInt32			providerSize;
                UInt32			providerSizeAfterMerge;

                // A capacity of 0 indicates that the dictionary should have the same size as the source
                //
                localCopyOfProvidersDictionary = OSDictionary::withDictionary( providerDictionary, 0);
                if ( localCopyOfProvidersDictionary == NULL )
                {
                    USBError(1,"%s::MergeDictionaryIntoProvider  could not copy our provider's dictionary",getName());
                    break;
                }
                
                // Get the size of our provider's dictionary so that we can check later whether it changed
                //
                providerSize = providerDictionary->getCapacity();
                USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  Created a local copy(%p) of dictionary (%p), size %d", getName(), this, localCopyOfProvidersDictionary, providerDictionary, (uint32_t)providerSize);
                
                USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  need to merge a dictionary (%s)", getName(), this, str);

                // Recursively merge the two dictionaries
                //
                result = MergeDictionaryIntoDictionary(  sourceDictionary, localCopyOfProvidersDictionary);
                if ( result )
                {
                    // Get the size of our provider's dictionary so to see if it's changed  (Yes, the size could remain the same but the contents
                    // could have changed, but this gives us a first approximation.  We're not doing anything with this result, although we could
                    // remerge if the size changed)
                    //
                    providerSizeAfterMerge = providerDictionary->getCapacity();
                    if ( providerSizeAfterMerge != providerSize )
                    {
                        USBError(1,"%s::MergeDictionaryIntoProvider  our provider's dictionary size changed (%d,%d)",getName(), (uint32_t)providerSize, (uint32_t)providerSizeAfterMerge);
                    }
                    
                    USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  setting  property %s from merged dictionary (%p)", getName(), this, str, providerDictionary);

                    // OK, now we can just set the property in our provider
                    //
                    result = provider->setProperty( dictionaryEntry, localCopyOfProvidersDictionary );
                    if ( !result )
                    {
                        USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  setProperty %s , returned false", getName(), this, str);
                        break;
                    }
                }
                else
                {
                    // If we got an error merging dictionaries, then just bail out without doing anything
                    //
                    USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  MergeDictionaryIntoDictionary(%p,%p) returned false", getName(), this, sourceDictionary, providerDictionary);
                    break;
                }
            }
            else
            {
                // Not a dictionary, so just set the property
                //
                USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  setting property %s", getName(), this, str);
                result = provider->setProperty(dictionaryEntry, dictionaryToMerge->getObject(dictionaryEntry));
                if ( !result )
                {
                    USBLog(6,"%s[%p]::MergeDictionaryIntoProvider  setProperty %s, returned false", getName(), this, str);
                    break;
                }
            }
        }
        iter->release();
    }
    USBLog(6,"-%s[%p]::MergeDictionaryIntoProvider(%p, %p)  result %d", getName(), this, provider, dictionaryToMerge, result);

    return result;
}
//================================================================================================
//
//   CheckSleepCapability
//
//================================================================================================
//
void
AppleUSBOHCI::CheckSleepCapability(void)
{
	// assume that sleep is OK
	_controllerCanSleep = true;
	_hasPCIPwrMgmt = false;
	
    //   We need to determine which OHCI controllers don't survive sleep.  These fall into 2 categories:
    //
    //   1.  CardBus cards
    //	 2.  PCI Cards that lose power (right now because of a bug in the PCI Family, USB PCI cards do not prevent
    //	     sleep, so even cards that don't support the PCI Power Mgmt stuff get their power removed.
    //
    //  Additionally, the PowerBook 101 controller cannot survive across sleep (I doesn't support remote wakeup).
    //
    //  So here, we look at all those cases and set the _unloadUIMAcrossSleep boolean to true.  As it turns out,
    //  if a controller does not have the "AAPL,clock-id" property, then it means that it cannot survive sleep.  We
    //  might need to refine this later once we figure how to deal with PCI cards that can go into PCI sleep mode.
    //  An exception is the B&W G3, that does not have this property but can sleep.  Sigh...
		
    //  Now, look at PCI cards.  Note that the onboard controller's provider is an IOPCIDevice so we cannot use that
    //  to distinguish between USB PCI cards and the on board controller.  Instead, we use the existence of the
    //  "AAPL,clock-id" property in the provider.  If it does not exist, then we are a OHCI controller on a USB PCI card.
    //
    if ( !_device->getProperty("AAPL,clock-id") && !((getPlatform()->getChipSetType() == kChipSetTypeGossamer) && getPlatform()->getMachineType() == kGossamerTypeYosemite) )
    {
		if (_device->getProperty("built-in"))
		{
			if (_errataBits & kErrataNECIncompleteWrite)
			{
				FixupNECControllerConfigRegisters();
			}

			// rdar://5769508 - if we are on a built in PCI device, then assume the system supports D3cold
			if (_device->hasPCIPowerManagement(kPCIPMCPMESupportFromD3Cold) && (_device->enablePCIPowerManagement(kPCIPMCSPowerStateD3) == kIOReturnSuccess))
			{
				_hasPCIPwrMgmt = true;
				setProperty("Card Type","Built-in");
			}
		}
		else
		{
			// rdar://5856545 - on older machines without the built-in property, we need to use the "default" case in the IOPCIDevice code
			if (_device->hasPCIPowerManagement() && (_device->enablePCIPowerManagement() == kIOReturnSuccess))
			{
				_hasPCIPwrMgmt = true;
				setProperty("Card Type","Built-in");
			}
		}
		
        if (!_hasPCIPwrMgmt)
        {
            USBError(1, "AppleUSBOHCI[%p]::CheckSleepCapability - controller will be unloaded across sleep",this);
            _controllerCanSleep = false;
            setProperty("Card Type","PCI");
        }
    }
    else
    {
        setProperty("Card Type","Built-in");
    }
    
	// if we have an ExpressCard attached (non-zero port), then we will need to disable port resume 
	// for that port (some cards disconnect when the ExpressCard power goes away and we would like to ignore these extra detach events.
	_ExpressCardPort = ExpressCardPort(_device);	
	_badExpressCardAttached = false;

	// Call registerService() so that the IOUSBController object is published and clients (like Prober) can find it
	registerService();
}
AppleEHCIitdMemoryBlock*
AppleEHCIitdMemoryBlock::NewMemoryBlock(void)
{
    AppleEHCIitdMemoryBlock 	*me = new AppleEHCIitdMemoryBlock;
    IOByteCount					len;
	IODMACommand				*dmaCommand = NULL;
	UInt64						offset = 0;
	IODMACommand::Segment32		segments;
	UInt32						numSegments = 1;
	IOReturn					status = kIOReturnSuccess;
    
    if (me)
	{
		// Use IODMACommand to get the physical address
		dmaCommand = IODMACommand::withSpecification(kIODMACommandOutputHost32, 32, PAGE_SIZE, (IODMACommand::MappingOptions)(IODMACommand::kMapped | IODMACommand::kIterateOnly));
		if (!dmaCommand)
		{
			USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not create IODMACommand");
			return NULL;
		}
		USBLog(6, "AppleEHCIitdMemoryBlock::NewMemoryBlock - got IODMACommand %p", dmaCommand);
		
		// allocate one page on a page boundary below the 4GB line
		me->_buffer = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(kernel_task, kIOMemoryUnshared | kIODirectionInOut, kEHCIPageSize, kEHCIStructureAllocationPhysicalMask);
		
		// allocate exactly one physical page
		if (me->_buffer) 
		{
			status = me->_buffer->prepare();
			if (status)
			{
				USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not prepare buffer");
				me->_buffer->release();
				me->_buffer = NULL;
				me->release();
				dmaCommand->release();
				return NULL;
			}
			me->_sharedLogical = (EHCIIsochTransferDescriptorSharedPtr)me->_buffer->getBytesNoCopy();
			bzero(me->_sharedLogical, kEHCIPageSize);
			status = dmaCommand->setMemoryDescriptor(me->_buffer);
			if (status)
			{
				USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not set memory descriptor");
				me->_buffer->complete();
				me->_buffer->release();
				me->_buffer = NULL;
				me->release();
				dmaCommand->release();
				return NULL;
			}
			status = dmaCommand->gen32IOVMSegments(&offset, &segments, &numSegments);
			dmaCommand->clearMemoryDescriptor();
			dmaCommand->release();
			if (status || (numSegments != 1) || (segments.fLength != kEHCIPageSize))
			{
				USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock - could not get physical segment");
				me->_buffer->complete();
				me->_buffer->release();
				me->_buffer = NULL;
				me->release();
				return NULL;
			}
			me->_sharedPhysical = segments.fIOVMAddr;
		}
		else
		{
			USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock, could not allocate buffer!");
			me->release();
			me = NULL;
		}
	}
	else
	{
		USBError(1, "AppleEHCIitdMemoryBlock::NewMemoryBlock, constructor failed!");
    }
    return me;
}
/**
 * \brief Isochronous transfer implementation
 *
 * Doing an isochrounous transfer for a certain number of bytes is
 * quite complicated. In an isochronous transfer, a packet is transmitted
 * in every micro frame, even if there is no new data available. So the
 * transfer has to be resubmitted each time a few packets have been received.
 * \param dev_handle	libusb_device_handle to use for the transfer
 */
void	IsoTransfer::submit(libusb_device_handle *dev_handle) throw(USBError) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "preparing isochronous transfer");

	// find the packet size that the endpoint can handle
	int	packetsize = endpoint->maxPacketSize()
			* endpoint->transactionOpportunities();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "found packet size: %d", packetsize);

	// each segment should be the same number of segments
	int	packets_per_segment = 400;

	// create a bunch of IsoSegments and add them to the queue
	debug(LOG_DEBUG, DEBUG_LOG, 0, "total packets: %d", totalpackets);
	int	packetcount = 0;
	while (packetcount < totalpackets) {
		IsoSegment	*segptr
			= new IsoSegment(endpoint, packets_per_segment,
				this, dev_handle, timeout);
		incoming.push(IsoSegmentPtr(segptr));
		packetcount += packets_per_segment;
	}
	debug(LOG_DEBUG, DEBUG_LOG, 0, "incoming now contains %d segments",
		incoming.size());
	if (incoming.size() == 0) {
		return;
	}

	// mark the transfer as incomplete
	complete = false;

	// lock the mutex, this will cause the thread to block when it starts
	lock.lock();
	
	// now create a new thread which will handle the events. But because
	// the mutex is locked, it will not start working just yet, only
	// when the mutex is unlocked, that thread will be released
	try {
		eventthread = std::thread(isotransfer_event_thread, this);
	} catch (...) {
		throw USBError("cannot create event handling thread");
	}

	// wait for completion of the request, using the condition variable
	// this will release the lock, so the thread will be released too
	try {
		condition.wait(lock);
	} catch (...) {
		throw USBError("cannot release event handling thread");
	}

	debug(LOG_DEBUG, DEBUG_LOG, 0, "all callbacks completed");

	// wait for the thread to terminate
	eventthread.join();

	// copy all the packets
	while (outgoing.size() > 0) {
		debug(LOG_DEBUG, DEBUG_LOG, 0,
			"extracting packets from segment");
		outgoing.front()->extract(packets);
		outgoing.pop();
	}
	debug(LOG_DEBUG, DEBUG_LOG, 0, "have now %d packets", packets.size());
}