/** * Tries to cancel a pending HGCM call. * * Return: VBox status code */ static int hgcm_cancel_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call) { int rc; /* * We use a pre-allocated request for cancellations, which is * protected by cancel_req_mutex. This means that all cancellations * get serialized, this should be fine since they should be rare. */ mutex_lock(&gdev->cancel_req_mutex); gdev->cancel_req->phys_req_to_cancel = virt_to_phys(call); rc = vbg_req_perform(gdev, gdev->cancel_req); mutex_unlock(&gdev->cancel_req_mutex); if (rc == VERR_NOT_IMPLEMENTED) { call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED; call->header.header.request_type = VMMDEVREQ_HGCM_CANCEL; rc = vbg_req_perform(gdev, call); if (rc == VERR_INVALID_PARAMETER) rc = VERR_NOT_FOUND; } if (rc >= 0) call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED; return rc; }
int vbg_hgcm_connect(struct vbg_dev *gdev, u32 requestor, struct vmmdev_hgcm_service_location *loc, u32 *client_id, int *vbox_status) { struct vmmdev_hgcm_connect *hgcm_connect = NULL; int rc; hgcm_connect = vbg_req_alloc(sizeof(*hgcm_connect), VMMDEVREQ_HGCM_CONNECT, requestor); if (!hgcm_connect) return -ENOMEM; hgcm_connect->header.flags = 0; memcpy(&hgcm_connect->loc, loc, sizeof(*loc)); hgcm_connect->client_id = 0; rc = vbg_req_perform(gdev, hgcm_connect); if (rc == VINF_HGCM_ASYNC_EXECUTE) wait_event(gdev->hgcm_wq, hgcm_req_done(gdev, &hgcm_connect->header)); if (rc >= 0) { *client_id = hgcm_connect->client_id; rc = hgcm_connect->header.result; } vbg_req_free(hgcm_connect, sizeof(*hgcm_connect)); *vbox_status = rc; return 0; }
int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 requestor, u32 client_id, int *vbox_status) { struct vmmdev_hgcm_disconnect *hgcm_disconnect = NULL; int rc; hgcm_disconnect = vbg_req_alloc(sizeof(*hgcm_disconnect), VMMDEVREQ_HGCM_DISCONNECT, requestor); if (!hgcm_disconnect) return -ENOMEM; hgcm_disconnect->header.flags = 0; hgcm_disconnect->client_id = client_id; rc = vbg_req_perform(gdev, hgcm_disconnect); if (rc == VINF_HGCM_ASYNC_EXECUTE) wait_event(gdev->hgcm_wq, hgcm_req_done(gdev, &hgcm_disconnect->header)); if (rc >= 0) rc = hgcm_disconnect->header.result; vbg_req_free(hgcm_disconnect, sizeof(*hgcm_disconnect)); *vbox_status = rc; return 0; }
/** * Callback for mouse events. * * This is called at the end of the ISR, after leaving the event spinlock, if * VMMDEV_EVENT_MOUSE_POSITION_CHANGED was raised by the host. * * @gdev: The device extension. */ void vbg_linux_mouse_event(struct vbg_dev *gdev) { int rc; /* Report events to the kernel input device */ gdev->mouse_status_req->mouse_features = 0; gdev->mouse_status_req->pointer_pos_x = 0; gdev->mouse_status_req->pointer_pos_y = 0; rc = vbg_req_perform(gdev, gdev->mouse_status_req); if (rc >= 0) { input_report_abs(gdev->input, ABS_X, gdev->mouse_status_req->pointer_pos_x); input_report_abs(gdev->input, ABS_Y, gdev->mouse_status_req->pointer_pos_y); input_sync(gdev->input); } }
/** * Performs the call and completion wait. * Return: 0 or negative errno value. * @gdev: The VBoxGuest device extension. * @call: The call to execute. * @timeout_ms: Timeout in ms. * @leak_it: Where to return the leak it / free it, indicator. * Cancellation fun. */ static int vbg_hgcm_do_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call, u32 timeout_ms, bool *leak_it) { int rc, cancel_rc, ret; long timeout; *leak_it = false; rc = vbg_req_perform(gdev, call); /* * If the call failed, then pretend success. Upper layers will * interpret the result code in the packet. */ if (rc < 0) { call->header.result = rc; return 0; } if (rc != VINF_HGCM_ASYNC_EXECUTE) return 0; /* Host decided to process the request asynchronously, wait for it */ if (timeout_ms == U32_MAX) timeout = MAX_SCHEDULE_TIMEOUT; else timeout = msecs_to_jiffies(timeout_ms); timeout = wait_event_interruptible_timeout( gdev->hgcm_wq, hgcm_req_done(gdev, &call->header), timeout); /* timeout > 0 means hgcm_req_done has returned true, so success */ if (timeout > 0) return 0; if (timeout == 0) ret = -ETIMEDOUT; else ret = -EINTR; /* Cancel the request */ cancel_rc = hgcm_cancel_call(gdev, call); if (cancel_rc >= 0) return ret; /* * Failed to cancel, this should mean that the cancel has lost the * race with normal completion, wait while the host completes it. */ if (cancel_rc == VERR_NOT_FOUND || cancel_rc == VERR_SEM_DESTROYED) timeout = msecs_to_jiffies(500); else timeout = msecs_to_jiffies(2000); timeout = wait_event_timeout(gdev->hgcm_wq, hgcm_req_done(gdev, &call->header), timeout); if (WARN_ON(timeout == 0)) { /* We really should never get here */ vbg_err("%s: Call timedout and cancellation failed, leaking the request\n", __func__); *leak_it = true; return ret; } /* The call has completed normally after all */ return 0; }