/*********************************************************** * Name: vchi_msg_queue * * Arguments: VCHI_SERVICE_HANDLE_T handle, * ssize_t (*copy_callback)(void *context, void *dest, * size_t offset, size_t maxsize), * void *context, * uint32_t data_size * * Description: Thin wrapper to queue a message onto a connection * * Returns: int32_t - success == 0 * ***********************************************************/ static int32_t vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, ssize_t (*copy_callback)(void *context, void *dest, size_t offset, size_t maxsize), void *context, uint32_t data_size) { struct shim_service *service = (struct shim_service *)handle; VCHIQ_STATUS_T status; while (1) { status = vchiq_queue_message(service->handle, copy_callback, context, data_size); /* * vchiq_queue_message() may return VCHIQ_RETRY, so we need to * implement a retry mechanism since this function is supposed * to block until queued */ if (status != VCHIQ_RETRY) break; msleep(1); } return vchiq_status_to_vchi(status); }
static void merge_flush(CLIENT_THREAD_STATE_T *thread) { assert(thread->merge_pos >= CLIENT_MAKE_CURRENT_SIZE); /* don't transmit just a make current -- in the case that there is only a make current in the merge buffer, we have already sent a control message for the rpc call (and with it a make current) and own the big lock */ if (thread->merge_pos > CLIENT_MAKE_CURRENT_SIZE) { VCHIQ_ELEMENT_T element; element.data = thread->merge_buffer; element.size = thread->merge_pos; VCHIQ_STATUS_T success = vchiq_queue_message(get_handle(thread), &element, 1); assert(success == VCHIQ_SUCCESS); thread->merge_pos = 0; client_send_make_current(thread); assert(thread->merge_pos == CLIENT_MAKE_CURRENT_SIZE); } }
int32_t vc_gpuserv_execute_code(int num_jobs, struct gpu_job_s jobs[]) { VCHIQ_ELEMENT_T elements[MAX_JOBS]; int i; // hack: temporarily allow calling this function without calling vc_gpuserv_init // will be removed later if (!gpuserv_client.service) { vc_gpuserv_init(); vcos_log_error("%s: called without calling vc_gpuserv_init", VCOS_FUNCTION); } if (!gpuserv_client.service) { vcos_log_error("%s: vchiq service not initialised", VCOS_FUNCTION); return -1; } if (num_jobs > MAX_JOBS) return -1; for (i=0; i<num_jobs; i++) { elements[i].data = jobs + i; elements[i].size = sizeof *jobs; } if (vchiq_queue_message(gpuserv_client.service, elements, num_jobs) != VCHIQ_SUCCESS) { goto error_exit; } return 0; error_exit: return -1; }
/** Send a message and do not wait for a reply. * * @note * This function should only be called from within a mmal component, so * vchiq_use/release_service calls aren't required (dealt with at higher level). * * @param client client to send message for * @param msg_header message header to send * @param size length of message, including header * @param msgid message id */ MMAL_STATUS_T mmal_vc_send_message(MMAL_CLIENT_T *client, mmal_worker_msg_header *msg_header, size_t size, uint8_t *data, size_t data_size, uint32_t msgid) { VCHIQ_STATUS_T vst; VCHIQ_ELEMENT_T elems[] = {{msg_header, size}}; MMAL_BOOL_T using_bulk_transfer = (data_size != 0); LOG_TRACE("len %d", data_size); vcos_assert(size >= sizeof(mmal_worker_msg_header)); if (!client->inited) { vcos_assert(0); return MMAL_EINVAL; } if (using_bulk_transfer) vcos_mutex_lock(&client->bulk_lock); msg_header->msgid = msgid; msg_header->magic = MMAL_MAGIC; vst = vchiq_queue_message(client->service, elems, 1); if (vst != VCHIQ_SUCCESS) { if (using_bulk_transfer) vcos_mutex_unlock(&client->bulk_lock); LOG_ERROR("failed"); goto error; } if (using_bulk_transfer) { LOG_TRACE("bulk transmit: %p, %i", data, data_size); data_size = (data_size + 3) & ~3; vst = vchiq_queue_bulk_transmit(client->service, data, data_size, msg_header); vcos_mutex_unlock(&client->bulk_lock); if (!vcos_verify(vst == VCHIQ_SUCCESS)) { LOG_ERROR("failed bulk transmit"); /* This really should not happen and if it does, things will go wrong as * we've already queued the vchiq message above. */ vcos_assert(0); goto error; } } return MMAL_SUCCESS; error: return MMAL_EIO; }
static void send_bulk(CLIENT_THREAD_STATE_T *thread, const void *in, uint32_t len) { if (len <= CTRL_THRESHOLD) { VCHIQ_ELEMENT_T element; element.data = in; element.size = len; VCHIQ_STATUS_T vchiq_status = vchiq_queue_message(get_handle(thread), &element, 1); assert(vchiq_status == VCHIQ_SUCCESS); } else { VCHIQ_STATUS_T vchiq_status = vchiq_queue_bulk_transmit(get_handle(thread), in, rpc_pad_bulk(len), NULL); assert(vchiq_status == VCHIQ_SUCCESS); VCOS_STATUS_T vcos_status = vcos_event_wait(&bulk_event); assert(vcos_status == VCOS_SUCCESS); } }
/** Send a message and wait for a reply. * * @param client client to send message for * @param msg_header message vchiq_header to send * @param size length of message, including header * @param msgid message id * @param dest destination for reply * @param destlen size of destination, updated with actual length * @param send_dummy_bulk whether to send a dummy bulk transfer */ MMAL_STATUS_T mmal_vc_sendwait_message(struct MMAL_CLIENT_T *client, mmal_worker_msg_header *msg_header, size_t size, uint32_t msgid, void *dest, size_t *destlen, MMAL_BOOL_T send_dummy_bulk) { MMAL_STATUS_T ret; MMAL_WAITER_T *waiter; VCHIQ_STATUS_T vst; VCHIQ_ELEMENT_T elems[] = {{msg_header, size}}; vcos_assert(size >= sizeof(mmal_worker_msg_header)); vcos_assert(dest); if (!client->inited) { vcos_assert(0); return MMAL_EINVAL; } if (send_dummy_bulk) vcos_mutex_lock(&client->bulk_lock); waiter = get_waiter(client); msg_header->msgid = msgid; msg_header->u.waiter = waiter; msg_header->magic = MMAL_MAGIC; waiter->dest = dest; waiter->destlen = *destlen; LOG_TRACE("wait %p, reply to %p", waiter, dest); mmal_vc_use_internal(client); vst = vchiq_queue_message(client->service, elems, 1); if (vst != VCHIQ_SUCCESS) { ret = MMAL_EIO; if (send_dummy_bulk) vcos_mutex_unlock(&client->bulk_lock); goto fail_msg; } if (send_dummy_bulk) { uint32_t data_size = 8; /* The data is just some dummy bytes so it's fine for it to be static */ static uint8_t data[8]; vst = vchiq_queue_bulk_transmit(client->service, data, data_size, msg_header); vcos_mutex_unlock(&client->bulk_lock); if (!vcos_verify(vst == VCHIQ_SUCCESS)) { LOG_ERROR("failed bulk transmit"); /* This really should not happen and if it does, things will go wrong as * we've already queued the vchiq message above. */ vcos_assert(0); ret = MMAL_EIO; goto fail_msg; } } /* now wait for the reply... * * FIXME: we could do with a timeout here. Need to be careful to cancel * the semaphore on a timeout. */ vcos_semaphore_wait(&waiter->sem); mmal_vc_release_internal(client); LOG_TRACE("got reply (len %i/%i)", (int)*destlen, (int)waiter->destlen); *destlen = waiter->destlen; release_waiter(client, waiter); return MMAL_SUCCESS; fail_msg: mmal_vc_release_internal(client); release_waiter(client, waiter); return ret; }
static int vc_watchdog_proc_write( struct file *file, const char __user *buffer, unsigned long count, void *data) { char *command; char kbuf[PROC_WRITE_BUF_SIZE + 1]; (void)file; memset(kbuf, 0, PROC_WRITE_BUF_SIZE + 1); if (count >= PROC_WRITE_BUF_SIZE) count = PROC_WRITE_BUF_SIZE; if (copy_from_user(kbuf, buffer, count) != 0) return -EFAULT; kbuf[count - 1] = 0; command = kbuf; if (strncmp("enable", command, strlen("enable")) == 0) { LOG_INFO("%s: Enabling VC watchdog", __func__); atomic_set(&vc_wdog_state->wdog_enabled, 1); complete_all(&vc_wdog_state->wdog_disable_blocker); } else if (strncmp("disable", command, strlen("disable")) == 0) { LOG_INFO("%s: Disabling VC watchdog", __func__); atomic_set(&vc_wdog_state->wdog_enabled, 0); init_completion(&vc_wdog_state->wdog_disable_blocker); } else if (strncmp("ping", command, strlen("ping")) == 0) { unsigned long msg = WDOG_PING_MSG; VCHIQ_ELEMENT_T elem = { .data = (void *)&msg, .size = sizeof(msg) }; long rc = 0; /* on-demand ping of videocore. Wake VC up to do this. */ LOG_INFO("%s: Pinging VC...", __func__); vchiq_use_service(vc_wdog_state->service_handle); if (mutex_lock_interruptible(&vc_wdog_state->wdog_ping_mutex) != 0) { LOG_ERR("%s: Interrupted waiting for watchdog", __func__); } else { /* ping vc... */ vchiq_queue_message(vc_wdog_state->service_handle, &elem, 1); /* ...and wait for the response with a timeout */ rc = wait_for_completion_interruptible_timeout( &vc_wdog_state->wdog_ping_response, msecs_to_jiffies(VC_PING_RESPONSE_TIMEOUT_MS)); if (rc == 0) LOG_ERR("%s VideoCore ping timed out!!", __func__); else if (rc < 0) LOG_ERR("%s: Interrupted waiting for ping", __func__); else LOG_INFO("%s: Ping response received", __func__); } mutex_unlock(&vc_wdog_state->wdog_ping_mutex); vchiq_release_service(vc_wdog_state->service_handle); } return count; }
static int vc_watchdog_thread_func(void *v) { while (1) { long rc; unsigned long msg = WDOG_PING_MSG; VCHIQ_ELEMENT_T elem = { .data = (void *)&msg, .size = sizeof(msg) }; int time_remaining = msecs_to_jiffies(WATCHDOG_PING_RATE_MS); LOG_DBG("%s: waiting on disable blocker...", __func__); if (wait_for_completion_interruptible( &vc_wdog_state->wdog_disable_blocker) != 0) { flush_signals(current); continue; } LOG_DBG("%s: Waiting for VC to be awake...", __func__); /* Ensure we only ping videocore when it's awake. Call use * service in a mode which will not initiate a wakeup */ vchiq_use_service_no_resume(vc_wdog_state->service_handle); if (!atomic_read(&vc_wdog_state->wdog_enabled)) { vchiq_release_service(vc_wdog_state->service_handle); LOG_DBG("%s: VC watchdog disabled", __func__); continue; } if (mutex_lock_interruptible(&vc_wdog_state->wdog_ping_mutex) != 0) { vchiq_release_service(vc_wdog_state->service_handle); LOG_DBG("%s: Interrupted waiting for ping", __func__); continue; } LOG_DBG("%s: Pinging videocore", __func__); /* ping vc... */ vchiq_queue_message(vc_wdog_state->service_handle, &elem, 1); LOG_DBG("%s: Waiting for ping response", __func__); /* ...and wait for the response with a timeout */ rc = wait_for_completion_interruptible_timeout( &vc_wdog_state->wdog_ping_response, msecs_to_jiffies(VC_PING_RESPONSE_TIMEOUT_MS)); if (rc == 0) { /* Timed out... BANG! */ vc_wdog_state->failed_pings++; LOG_ERR("%s VideoCore Watchdog timed out!! (%d)", __func__, vc_wdog_state->failed_pings); if (vc_wdog_state->failed_pings >= WATCHDOG_NO_RESPONSE_COUNT) BUG(); } else if (rc < 0) LOG_ERR("%s: Interrupted waiting for ping", __func__); else { LOG_DBG("%s: Ping response received", __func__); vc_wdog_state->failed_pings = 0; } mutex_unlock(&vc_wdog_state->wdog_ping_mutex); vchiq_release_service(vc_wdog_state->service_handle); LOG_DBG("%s: waiting before pinging again...", __func__); /* delay before running again */ do { set_current_state(TASK_INTERRUPTIBLE); time_remaining = schedule_timeout(time_remaining); if (time_remaining) { LOG_ERR("%s interrupted", __func__); flush_signals(current); } } while (time_remaining > 0); } return 0; } static void vc_watchdog_connected_init(void) { int ret = 0; VCHIQ_SERVICE_PARAMS_T vchiq_params = { .fourcc = VCHIQ_MAKE_FOURCC('W', 'D', 'O', 'G'), .callback = vc_watchdog_vchiq_callback, .version = VC_WDOG_VERSION, .version_min = VC_WDOG_VERSION_MIN }; LOG_INFO("%s: start", __func__); /* Initialize and create a VCHIQ connection */ ret = vchiq_initialise(&vc_wdog_state->initialise_instance); if (ret != 0) { LOG_ERR("%s: failed to initialise VCHIQ instance (ret=%d)", __func__, ret); ret = -EIO; goto out; } ret = vchiq_connect(vc_wdog_state->initialise_instance); if (ret != 0) { LOG_ERR("%s: failed to connect VCHIQ instance (ret=%d)", __func__, ret); ret = -EIO; goto out; } ret = vchiq_open_service(vc_wdog_state->initialise_instance, &vchiq_params, &vc_wdog_state->service_handle); if (ret != 0 || (vc_wdog_state->service_handle == 0)) { LOG_ERR("%s: failed to add WDOG service: error %d", __func__, ret); ret = -EPERM; goto out; } vchiq_release_service(vc_wdog_state->service_handle); init_completion(&vc_wdog_state->wdog_ping_response); mutex_init(&vc_wdog_state->wdog_ping_mutex); init_completion(&vc_wdog_state->wdog_disable_blocker); #ifdef ENABLE_VC_WATCHDOG complete_all(&vc_wdog_state->wdog_disable_blocker); atomic_set(&vc_wdog_state->wdog_enabled, 1); #else atomic_set(&vc_wdog_state->wdog_enabled, 0); #endif vc_wdog_state->wdog_thread = kthread_create( &vc_watchdog_thread_func, NULL, "vc-watchdog"); if (vc_wdog_state->wdog_thread == NULL) LOG_ERR("FATAL: couldn't create thread vc-watchdog"); else wake_up_process(vc_wdog_state->wdog_thread); out: LOG_INFO("%s: end (ret=%d)", __func__, ret); } static int vc_watchdog_probe(struct platform_device *p_dev) { int ret = 0; LOG_INFO("%s: start", __func__); vc_wdog_state = kzalloc(sizeof(struct vc_watchdog_state), GFP_KERNEL); if (!vc_wdog_state) { ret = -ENOMEM; goto exit; } vc_wdog_state->p_dev = p_dev; vc_wdog_state->proc_entry = create_proc_entry(DRIVER_NAME, 0444, NULL); if (vc_wdog_state->proc_entry == NULL) { ret = -EFAULT; goto out_efault; } vc_wdog_state->proc_entry->data = (void *)vc_wdog_state; vc_wdog_state->proc_entry->write_proc = vc_watchdog_proc_write; vchiq_add_connected_callback(vc_watchdog_connected_init); goto exit; out_efault: kfree(vc_wdog_state); vc_wdog_state = NULL; exit: LOG_INFO("%s: end, ret=%d", __func__, ret); return ret; }