示例#1
0
/***********************************************************
 * 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);
    }
}
示例#3
0
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;
}
示例#4
0
/** 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);
    }
}
示例#6
0
/** 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;
}
示例#7
0
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;
}
示例#8
0
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;
}