IMG_VOID SYSDEVU_GetMemoryInfo( IMG_UINT32 ui32DeviceId, IMG_VOID ** ppvKmMemory, IMG_PHYSADDR * ppaPhysMemory, IMG_UINT64 * pui64MemSize, IMG_UINT64 * pui64DevMemoryBase ) { #ifndef __FAKE_HW__ SYSDEVU_sInfo *dev = findDeviceById(ui32DeviceId); if(!gSysDevInitialised || !dev) { IMG_ASSERT(gSysDevInitialised); IMG_ASSERT(dev); return; } if(ppvKmMemory) *ppvKmMemory = dev->pui32KmMemBase; if(ppaPhysMemory) *ppaPhysMemory = dev->paPhysMemBase; if(pui64MemSize) *pui64MemSize = dev->ui32MemSize; if(pui64DevMemoryBase) *pui64DevMemoryBase = (IMG_UINT64)dev->pui64DevMemoryBase; #endif }
/*! ****************************************************************************** @Function SYSDEVU_InvokeDevKmLisr ******************************************************************************/ IMG_RESULT SYSDEVU_InvokeDevKmLisr( IMG_UINT32 ui32DeviceId ) { IMG_UINT32 ui32Result; SYSDEVU_sInfo *dev = findDeviceById(ui32DeviceId); if(!gSysDevInitialised || !dev) { IMG_ASSERT(gSysDevInitialised); IMG_ASSERT(dev); ui32Result = IMG_ERROR_INVALID_PARAMETERS; return ui32Result; } IMG_ASSERT(dev->pfnDevKmLisr != IMG_NULL); if(dev->pfnDevKmLisr == IMG_NULL) { ui32Result = IMG_ERROR_INVALID_PARAMETERS; return ui32Result; } dev->pfnDevKmLisr(dev->pvParam); return IMG_SUCCESS; }
int freespace_private_send(FreespaceDeviceId id, const uint8_t* message, int length) { int rc; int count; struct FreespaceDevice* device; device = findDeviceById(id); if (device == NULL || device->state_ != FREESPACE_OPENED) { return FREESPACE_ERROR_NOT_FOUND; } if (length > device->maxWriteSize_) { // Can't write more than the max allowed size, so fail rather than send a partial packet. return FREESPACE_ERROR_SEND_TOO_LARGE; } rc = libusb_interrupt_transfer(device->handle_, device->writeEndpointAddress_, (unsigned char*) message, length, &count, 0); if (rc != LIBUSB_SUCCESS) { return libusb_to_freespace_error(rc); } if (length != count) { // libusb should never fragment the message. return FREESPACE_ERROR_UNEXPECTED; } return FREESPACE_SUCCESS; }
/*! ****************************************************************************** @Function SYSDEVU_RegisterDevKmLisr ******************************************************************************/ IMG_VOID SYSDEVU_RegisterDevKmLisr( IMG_UINT32 ui32DeviceId, SYSDEVKM_pfnDevKmLisr pfnDevKmLisr, IMG_VOID * pvParam ) { SYSDEVU_sInfo *dev = findDeviceById(ui32DeviceId); if(!gSysDevInitialised || !dev) { IMG_ASSERT(gSysDevInitialised); IMG_ASSERT(dev); return; } if ( (pfnDevKmLisr != IMG_NULL) && (dev->pfnDevKmLisr != IMG_NULL) ) { IMG_ASSERT(dev->pfnDevKmLisr == pfnDevKmLisr); IMG_ASSERT(dev->pvParam == pvParam); } else { dev->pfnDevKmLisr = pfnDevKmLisr; dev->pvParam = pvParam; } }
void freespace_closeDevice(FreespaceDeviceId id) { struct FreespaceDevice* device; device = findDeviceById(id); if (device != NULL && device->handle_ != NULL) { // Stop receives. freespace_terminateReceiveTransfers(device); // Should we wait until everything terminates cleanly? // Release our lock on the interface. libusb_release_interface(device->handle_, device->api_->controlInterfaceNumber_); // Re-attach the kernel driver if we detached it before. if (device->kernelDriverDetached_) { // This currently fails, and there doesn't seem to be anything that we // can do. libusb_attach_kernel_driver(device->handle_, device->api_->controlInterfaceNumber_); } libusb_close(device->handle_); device->handle_ = NULL; if (device->state_ == FREESPACE_DISCONNECTED) { removeFreespaceDevice(device); } else { device->state_ = FREESPACE_CONNECTED; } } }
int freespace_getDeviceInfo(FreespaceDeviceId id, struct FreespaceDeviceInfo* info) { struct FreespaceDevice* device = findDeviceById(id); if (device != NULL) { info->vendor = device->idVendor_; info->product = device->idProduct_; info->name = device->api_->name_; info->hVer = device->api_->hVer_; return FREESPACE_SUCCESS; } else { return FREESPACE_ERROR_NOT_FOUND; } }
int freespace_setReceiveMessageCallback(FreespaceDeviceId id, freespace_receiveMessageCallback callback, void* cookie) { struct FreespaceDevice* device = findDeviceById(id); int wereInSyncMode; int rc; if (device == NULL) { return FREESPACE_ERROR_NOT_FOUND; } wereInSyncMode = (device->receiveMessageCallback_ == NULL && device->receiveCallback_ == NULL); device->receiveMessageCallback_ = callback; device->receiveMessageCookie_ = cookie; if (callback != NULL && wereInSyncMode && device->state_ == FREESPACE_OPENED) { struct freespace_message m; // Transition from sync mode to async mode. // Need to run the callback on all received messages. struct FreespaceReceiveTransfer* rt; rt = &device->receiveQueue_[device->receiveQueueHead_]; while (rt->submitted_ == 0) { rc = freespace_decode_message((const uint8_t*) rt->buffer_, rt->transfer_->actual_length, &m, device->api_->hVer_); if (rc == FREESPACE_SUCCESS) { callback(device->id_, &m, cookie, libusb_transfer_status_to_freespace_error(rt->transfer_->status)); } else { callback(device->id_, NULL, cookie, rc); } rt->submitted_ = 1; libusb_submit_transfer(rt->transfer_); device->receiveQueueHead_++; if (device->receiveQueueHead_ >= FREESPACE_RECEIVE_QUEUE_SIZE) { device->receiveQueueHead_ = 0; } rt = &device->receiveQueue_[device->receiveQueueHead_]; } } return FREESPACE_SUCCESS; }
IMG_VOID SYSDEVU_FreeDevice( IMG_UINT32 ui32DeviceId ) { SYSDEVU_sInfo *dev = findDeviceById(ui32DeviceId); if(!gSysDevInitialised || !dev) { IMG_ASSERT(gSysDevInitialised); IMG_ASSERT(dev); return; } if (dev->ops->free_device) dev->ops->free_device(dev); }
IMG_VOID SYSDEVU_HandleResume( IMG_UINT32 ui32DeviceId, IMG_BOOL forAPM ) { SYSDEVU_sInfo *dev = findDeviceById(ui32DeviceId); if(!gSysDevInitialised || !dev) { IMG_ASSERT(gSysDevInitialised); IMG_ASSERT(dev); return; } if(dev->ops->resume_device) dev->ops->resume_device(dev, forAPM); }
/** * Request capture of a specific device. * * This is in an interface for SessionMachine::CaptureUSBDevice(), which is * an internal worker used by Console::AttachUSBDevice() from the VM process. * * When the request is completed, SessionMachine::onUSBDeviceAttach() will * be called for the given machine object. * * * @param aMachine The machine to attach the device to. * @param aId The UUID of the USB device to capture and attach. * * @returns COM status code and error info. * * @remarks This method may operate synchronously as well as asynchronously. In the * former case it will temporarily abandon locks because of IPC. */ HRESULT USBProxyService::captureDeviceForVM(SessionMachine *aMachine, IN_GUID aId) { ComAssertRet(aMachine, E_INVALIDARG); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* * Translate the device id into a device object. */ ComObjPtr<HostUSBDevice> pHostDevice = findDeviceById(aId); if (pHostDevice.isNull()) return setError(E_INVALIDARG, tr("The USB device with UUID {%RTuuid} is not currently attached to the host"), Guid(aId).raw()); /* * Try to capture the device */ alock.release(); return pHostDevice->requestCaptureForVM(aMachine, true /* aSetError */); }
/** * Notification from VM process about USB device detaching progress. * * This is in an interface for SessionMachine::DetachUSBDevice(), which is * an internal worker used by Console::DetachUSBDevice() from the VM process. * * @param aMachine The machine which is sending the notification. * @param aId The UUID of the USB device is concerns. * @param aDone \a false for the pre-action notification (necessary * for advancing the device state to avoid confusing * the guest). * \a true for the post-action notification. The device * will be subjected to all filters except those of * of \a Machine. * * @returns COM status code. * * @remarks When \a aDone is \a true this method may end up doing IPC to other * VMs when running filters. In these cases it will temporarily * abandon its locks. */ HRESULT USBProxyService::detachDeviceFromVM(SessionMachine *aMachine, IN_GUID aId, bool aDone) { LogFlowThisFunc(("aMachine=%p{%s} aId={%RTuuid} aDone=%RTbool\n", aMachine, aMachine->getName().c_str(), Guid(aId).raw(), aDone)); // get a list of all running machines while we're outside the lock // (getOpenedMachines requests locks which are incompatible with the lock of the machines list) SessionMachinesList llOpenedMachines; mHost->parent()->getOpenedMachines(llOpenedMachines); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); ComObjPtr<HostUSBDevice> pHostDevice = findDeviceById(aId); ComAssertRet(!pHostDevice.isNull(), E_FAIL); AutoWriteLock devLock(pHostDevice COMMA_LOCKVAL_SRC_POS); /* * Work the state machine. */ LogFlowThisFunc(("id={%RTuuid} state=%s aDone=%RTbool name={%s}\n", pHostDevice->getId().raw(), pHostDevice->getStateName(), aDone, pHostDevice->getName().c_str())); bool fRunFilters = false; HRESULT hrc = pHostDevice->onDetachFromVM(aMachine, aDone, &fRunFilters); /* * Run filters if necessary. */ if ( SUCCEEDED(hrc) && fRunFilters) { Assert(aDone && pHostDevice->getUnistate() == kHostUSBDeviceState_HeldByProxy && pHostDevice->getMachine().isNull()); devLock.release(); alock.release(); HRESULT hrc2 = runAllFiltersOnDevice(pHostDevice, llOpenedMachines, aMachine); ComAssertComRC(hrc2); } return hrc; }
int freespace_flush(FreespaceDeviceId id) { struct FreespaceDevice* device = findDeviceById(id); struct FreespaceReceiveTransfer* rt; struct timeval tv; int repeat; int maxRepeats = FREESPACE_RECEIVE_QUEUE_SIZE * 2; if (device == NULL || device->state_ != FREESPACE_OPENED) { return FREESPACE_ERROR_NOT_FOUND; } // As long as there's work, try again. do { // Poll libusb to give it a chance to unload as many // events as possible. tv.tv_sec = 0; tv.tv_usec = 0; libusb_handle_events_timeout(freespace_libusb_context, &tv); repeat = 0; // Clear out our queue. rt = &device->receiveQueue_[device->receiveQueueHead_]; while (rt->submitted_ == 0) { rt->submitted_ = 1; libusb_submit_transfer(rt->transfer_); device->receiveQueueHead_++; if (device->receiveQueueHead_ >= FREESPACE_RECEIVE_QUEUE_SIZE) { device->receiveQueueHead_ = 0; } rt = &device->receiveQueue_[device->receiveQueueHead_]; repeat = 1; } maxRepeats--; } while (repeat > 0 && maxRepeats > 0); return FREESPACE_SUCCESS; }
/*! ****************************************************************************** @Function SYSDEVU_GetCpuAddrs ******************************************************************************/ IMG_UINT32 SYSDEVU_GetCpuAddrs( IMG_UINT32 ui32DeviceId, SYSDEVKM_eRegionId eRegionId, IMG_VOID ** ppvCpuKmAddr, IMG_PHYSADDR * ppaCpuPhysAddr, IMG_UINT32 * pui32Size ) { SYSDEVU_sInfo *dev = findDeviceById(ui32DeviceId); if(!gSysDevInitialised || !dev) { IMG_ASSERT(gSysDevInitialised); return IMG_ERROR_DEVICE_NOT_FOUND; } #ifdef __FAKE_HW__ /* provide fake kernel mode addresses for fake hardware */ gpsInfo[ui32DeviceId]->pui32KmRegBase = (void*)0x1000; gpsInfo[ui32DeviceId]->ui32RegSize = 0x1000; #endif switch (eRegionId) { case SYSDEVKM_REGID_REGISTERS: if(ppvCpuKmAddr) *ppvCpuKmAddr = dev->pui32KmRegBase; if(ppaCpuPhysAddr) *ppaCpuPhysAddr = dev->paPhysRegBase; if(pui32Size) *pui32Size = dev->ui32RegSize; return IMG_SUCCESS; break; case SYSDEVKM_REGID_SLAVE_PORT: default: IMG_ASSERT(IMG_FALSE); break; } return IMG_ERROR_GENERIC_FAILURE; }
SYSDEVU_sInfo *SYSDEVU_GetDeviceById( IMG_UINT32 ui32DeviceId ) { return findDeviceById(ui32DeviceId); }
int freespace_private_sendAsync(FreespaceDeviceId id, const uint8_t* message, int length, unsigned int timeoutMs, freespace_sendCallback callback, void* cookie) { #ifdef __APPLE__ // @TODO: Figure out why libusb on darwin doesn't seem to work with asynchronous messages int rc; rc = freespace_private_send(id, message, length); if (callback != NULL) { callback(id, cookie, rc); } return libusb_to_freespace_error(rc); #else struct FreespaceDevice* device; device = findDeviceById(id); struct libusb_transfer* transfer; int rc; if (device == NULL || device->state_ != FREESPACE_OPENED) { return FREESPACE_ERROR_NOT_FOUND; } if (length > device->maxWriteSize_) { return FREESPACE_ERROR_SEND_TOO_LARGE; } transfer = libusb_alloc_transfer(0); if (transfer == NULL) { return FREESPACE_ERROR_OUT_OF_MEMORY; } transfer->dev_handle = device->handle_; transfer->endpoint = device->writeEndpointAddress_; transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; transfer->timeout = timeoutMs; transfer->buffer = (unsigned char*) message; transfer->length = length; transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; if (callback != NULL) { struct SendTransferInfo* info = (struct SendTransferInfo*) malloc(sizeof(struct SendTransferInfo)); if (info == NULL) { libusb_free_transfer(transfer); return FREESPACE_ERROR_OUT_OF_MEMORY; } info->id = id; info->callback = callback; info->cookie = cookie; transfer->callback = sendCallback; transfer->user_data = info; } else { transfer->callback = NULL; transfer->user_data = NULL; } rc = libusb_submit_transfer(transfer); return libusb_to_freespace_error(rc); #endif }
int freespace_private_read(FreespaceDeviceId id, uint8_t* message, int maxLength, unsigned int timeoutMs, int* actualLength) { struct FreespaceDevice* device = findDeviceById(id); struct FreespaceReceiveTransfer* rt; int rc; if (device == NULL || device->state_ != FREESPACE_OPENED) { return FREESPACE_ERROR_NOT_FOUND; } if (maxLength < device->maxReadSize_) { // Don't risk causing an overflow due to too small // a receive buffer. return FREESPACE_ERROR_RECEIVE_BUFFER_TOO_SMALL; } rt = &device->receiveQueue_[device->receiveQueueHead_]; // Check if we need to wait. if (rt->submitted_ != 0) { struct timeval tv; tv.tv_sec = timeoutMs / 1000; tv.tv_usec = (timeoutMs % 1000) * 1000; // Wait. do { rc = libusb_handle_events_timeout(freespace_libusb_context, &tv); if (rc != LIBUSB_SUCCESS) { return libusb_to_freespace_error(rc); } // Keep trying until something has been received. // Note that libusb_handle_events_timeout could return // without a receive if it ends up doing some other // processing such as an async send completion or // something on another device. // TODO: update tv with time left. timeoutMs = 0; } while (rt->submitted_ != 0 && timeoutMs > 0); if (rt->submitted_ != 0) { return LIBUSB_ERROR_TIMEOUT; } } // Copy the message out. *actualLength = rt->transfer_->actual_length; memcpy(message, rt->buffer_, *actualLength); rc = libusb_transfer_status_to_freespace_error(rt->transfer_->status); // Resubmit the transfer rt->submitted_ = 1; libusb_submit_transfer(rt->transfer_); device->receiveQueueHead_++; if (device->receiveQueueHead_ >= FREESPACE_RECEIVE_QUEUE_SIZE) { device->receiveQueueHead_ = 0; } return rc; }
static int scanDevices() { struct libusb_device** devs; ssize_t count; ssize_t i; int rc; int needToRescan; // Check if the devices need to be rescanned. rc = freespace_hotplug_perform(&needToRescan); if (rc != FREESPACE_SUCCESS || !needToRescan) { return rc; } count = libusb_get_device_list(freespace_libusb_context, &devs); if (count < 0) { return libusb_to_freespace_error(count); } ts++; for (i = 0; i < count; i++) { struct libusb_device_descriptor desc; struct libusb_device* dev = devs[i]; struct FreespaceDeviceAPI const * api; rc = libusb_get_device_descriptor(dev, &desc); if (rc < 0) { // Can't get the device descriptor, so try the next one. continue; } // Find if this device is in the known list. api = lookupDevice(&desc); if (api != NULL) { uint8_t deviceAddress = libusb_get_device_address(dev); struct FreespaceDevice* device; device = findDeviceById(deviceAddress); if (device == NULL) { device = (struct FreespaceDevice*) malloc(sizeof(struct FreespaceDevice)); if (device == NULL) { // Out of memory. libusb_free_device_list(devs, 1); return FREESPACE_ERROR_OUT_OF_MEMORY; } memset(device, 0, sizeof(struct FreespaceDevice)); libusb_ref_device(dev); device->dev_ = dev; device->idProduct_ = desc.idProduct; device->idVendor_ = desc.idVendor; device->api_ = api; device->id_ = libusb_get_device_address(dev); device->state_ = FREESPACE_CONNECTED; device->ts_ = ts; addFreespaceDevice(device); if (hotplugCallback) { hotplugCallback(FREESPACE_HOTPLUG_INSERTION, device->id_, hotplugCookie); } } device->ts_ = ts; } } for (i = 0; i < FREESPACE_MAXIMUM_DEVICE_COUNT; i++) { struct FreespaceDevice* d = devices[i]; if (d != NULL && d->ts_ != ts) { if (hotplugCallback) { hotplugCallback(FREESPACE_HOTPLUG_REMOVAL, d->id_, hotplugCookie); } if (d->state_ == FREESPACE_OPENED) { d->state_ = FREESPACE_DISCONNECTED; } else { removeFreespaceDevice(d); } } } libusb_free_device_list(devs, 1); return FREESPACE_SUCCESS; }
int freespace_openDevice(FreespaceDeviceId id) { struct FreespaceDevice* device = findDeviceById(id); struct libusb_config_descriptor *config; const struct libusb_interface_descriptor* intd; int controlInterfaceNumber; int i; int rc; if (device == NULL) { return FREESPACE_ERROR_NOT_FOUND; } rc = libusb_open(device->dev_, &device->handle_); if (rc != LIBUSB_SUCCESS) { return libusb_to_freespace_error(rc); } controlInterfaceNumber = device->api_->controlInterfaceNumber_; rc = libusb_kernel_driver_active(device->handle_, controlInterfaceNumber); if (rc == 1) { // Kernel driver attached. Disconnect it. rc = libusb_detach_kernel_driver(device->handle_, controlInterfaceNumber); if (rc == LIBUSB_SUCCESS) { device->kernelDriverDetached_ = 1; } } rc = libusb_claim_interface(device->handle_, controlInterfaceNumber); if (rc != LIBUSB_SUCCESS) { return libusb_to_freespace_error(rc); } rc = libusb_get_active_config_descriptor(device->dev_, &config); if (rc != LIBUSB_SUCCESS) { return libusb_to_freespace_error(rc); } intd = config->interface[controlInterfaceNumber].altsetting; for (i = 0; i < intd[0].bNumEndpoints; ++i) { const struct libusb_endpoint_descriptor* endpoint = &intd[0].endpoint[i]; if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) { device->readEndpointAddress_ = endpoint->bEndpointAddress; device->maxReadSize_ = endpoint->wMaxPacketSize; } else { device->writeEndpointAddress_ = endpoint->bEndpointAddress; device->maxWriteSize_ = endpoint->wMaxPacketSize; } } if (device->maxReadSize_ == 0 || device->maxWriteSize_ == 0) { // Weird. The device didn't have a read and write endpoint. return FREESPACE_ERROR_UNEXPECTED; } device->state_ = FREESPACE_OPENED; // Start the receive queue working. rc = freespace_initiateReceiveTransfers(device); return rc; }