162607Sitojun/**
278064Sume * Copyright (c) 2010-2012 Broadcom. All rights reserved.
355163Sshin *
455163Sshin * Redistribution and use in source and binary forms, with or without
555163Sshin * modification, are permitted provided that the following conditions
655163Sshin * are met:
762607Sitojun * 1. Redistributions of source code must retain the above copyright
855163Sshin *    notice, this list of conditions, and the following disclaimer,
955163Sshin *    without modification.
1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer in the
1255163Sshin *    documentation and/or other materials provided with the distribution.
1355163Sshin * 3. The names of the above-listed copyright holders may not be used
1455163Sshin *    to endorse or promote products derived from this software without
1555163Sshin *    specific prior written permission.
1655163Sshin *
1755163Sshin * ALTERNATIVELY, this software may be distributed under the terms of the
1855163Sshin * GNU General Public License ("GPL") version 2, as published by the Free
1962607Sitojun * Software Foundation.
2055163Sshin *
2155163Sshin * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
2255163Sshin * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
2355163Sshin * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2455163Sshin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
2555163Sshin * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2655163Sshin * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2755163Sshin * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2855163Sshin * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2955163Sshin * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3055163Sshin * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3155163Sshin * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3255163Sshin */
3355163Sshin
3478064Sume/* ---- Include Files ---------------------------------------------------- */
3555163Sshin
3655163Sshin#include "vchiq_core.h"
3755163Sshin#include "vchiq_arm.h"
3855163Sshin#include "vchiq_killable.h"
3955163Sshin
4055163Sshin/* ---- Public Variables ------------------------------------------------- */
4155163Sshin
4255163Sshin/* ---- Private Constants and Types -------------------------------------- */
4355163Sshin
4455163Sshinstruct bulk_waiter_node {
4555163Sshin	struct bulk_waiter bulk_waiter;
4655163Sshin	int pid;
4755163Sshin	struct list_head list;
4855163Sshin};
4955163Sshin
5055163Sshinstruct vchiq_instance_struct {
5162607Sitojun	VCHIQ_STATE_T *state;
5255163Sshin
53119076Sume	int connected;
54119076Sume
55119076Sume	struct list_head bulk_waiter_list;
5655163Sshin	struct mutex bulk_waiter_list_mutex;
5755163Sshin};
5855163Sshin
5955163Sshinstatic VCHIQ_STATUS_T
6055163Sshinvchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
6155163Sshin	unsigned int size, VCHIQ_BULK_DIR_T dir);
6255163Sshin
6355163Sshin/****************************************************************************
6455163Sshin*
6562607Sitojun*   vchiq_initialise
6655163Sshin*
6762607Sitojun***************************************************************************/
6862607Sitojun#define VCHIQ_INIT_RETRIES 10
6962607SitojunVCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut)
7055163Sshin{
7162607Sitojun	VCHIQ_STATUS_T status = VCHIQ_ERROR;
7262607Sitojun	VCHIQ_STATE_T *state;
7355163Sshin	VCHIQ_INSTANCE_T instance = NULL;
7455163Sshin        int i;
7555163Sshin
7655163Sshin	vchiq_log_trace(vchiq_core_log_level, "%s called", __func__);
7755163Sshin
7862607Sitojun        /* VideoCore may not be ready due to boot up timing.
7955163Sshin           It may never be ready if kernel and firmware are mismatched, so don't block forever. */
8055163Sshin        for (i=0; i<VCHIQ_INIT_RETRIES; i++) {
8155163Sshin		state = vchiq_get_state();
8255163Sshin		if (state)
8355163Sshin			break;
8455163Sshin		udelay(500);
8555163Sshin	}
8655163Sshin	if (i==VCHIQ_INIT_RETRIES) {
8755163Sshin		vchiq_log_error(vchiq_core_log_level,
8855163Sshin			"%s: videocore not initialized\n", __func__);
89119039Sume		goto failed;
9055163Sshin	} else if (i>0) {
9155163Sshin		vchiq_log_warning(vchiq_core_log_level,
9255163Sshin			"%s: videocore initialized after %d retries\n", __func__, i);
9362607Sitojun	}
9455163Sshin
9562607Sitojun	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
9655163Sshin	if (!instance) {
9755163Sshin		vchiq_log_error(vchiq_core_log_level,
9855163Sshin			"%s: error allocating vchiq instance\n", __func__);
9955163Sshin		goto failed;
10055163Sshin	}
10155163Sshin
10255163Sshin	instance->connected = 0;
10355163Sshin	instance->state = state;
10455163Sshin	lmutex_init(&instance->bulk_waiter_list_mutex);
10555163Sshin	INIT_LIST_HEAD(&instance->bulk_waiter_list);
10655163Sshin
10755163Sshin	*instanceOut = instance;
10855163Sshin
10955163Sshin	status = VCHIQ_SUCCESS;
11055163Sshin
11155163Sshinfailed:
11255163Sshin	vchiq_log_trace(vchiq_core_log_level,
11355163Sshin		"%s(%p): returning %d", __func__, instance, status);
11455163Sshin
11578064Sume	return status;
11678064Sume}
11755163SshinEXPORT_SYMBOL(vchiq_initialise);
11855163Sshin
11955163Sshin/****************************************************************************
12055163Sshin*
12155163Sshin*   vchiq_shutdown
12255163Sshin*
12355163Sshin***************************************************************************/
12462607Sitojun
12555163SshinVCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance)
12655163Sshin{
12755163Sshin	VCHIQ_STATUS_T status;
12855163Sshin	VCHIQ_STATE_T *state = instance->state;
12955163Sshin
13055163Sshin	vchiq_log_trace(vchiq_core_log_level,
13155163Sshin		"%s(%p) called", __func__, instance);
13255163Sshin
13355163Sshin	if (lmutex_lock_interruptible(&state->mutex) != 0)
13455163Sshin		return VCHIQ_RETRY;
13555163Sshin
13655163Sshin	/* Remove all services */
13755163Sshin	status = vchiq_shutdown_internal(state, instance);
13855163Sshin
13955163Sshin	lmutex_unlock(&state->mutex);
14055163Sshin
14155163Sshin	vchiq_log_trace(vchiq_core_log_level,
14255163Sshin		"%s(%p): returning %d", __func__, instance, status);
14355163Sshin
144119076Sume	if (status == VCHIQ_SUCCESS) {
145119076Sume		struct list_head *pos, *next;
146119076Sume		list_for_each_safe(pos, next,
147119070Sume				&instance->bulk_waiter_list) {
148119070Sume			struct bulk_waiter_node *waiter;
149119070Sume			waiter = list_entry(pos,
150119070Sume					struct bulk_waiter_node,
151119076Sume					list);
15255163Sshin			list_del(pos);
15355163Sshin			vchiq_log_info(vchiq_arm_log_level,
15455163Sshin					"bulk_waiter - cleaned up %x "
15555163Sshin					"for pid %d",
15655163Sshin					(unsigned int)waiter, waiter->pid);
15755163Sshin			_sema_destroy(&waiter->bulk_waiter.event);
15878064Sume
15955163Sshin			kfree(waiter);
16078064Sume		}
16155163Sshin
16255163Sshin		lmutex_destroy(&instance->bulk_waiter_list_mutex);
16355163Sshin
16478064Sume		kfree(instance);
16555163Sshin	}
16655163Sshin
16755163Sshin	return status;
16855163Sshin}
16955163SshinEXPORT_SYMBOL(vchiq_shutdown);
17055163Sshin
17155163Sshin/****************************************************************************
17255163Sshin*
17355163Sshin*   vchiq_is_connected
17462607Sitojun*
17562607Sitojun***************************************************************************/
17655163Sshin
17755163Sshinstatic int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
17855163Sshin{
17955163Sshin	return instance->connected;
18055163Sshin}
18155163Sshin
18255163Sshin/****************************************************************************
18355163Sshin*
18455163Sshin*   vchiq_connect
18555163Sshin*
18655163Sshin***************************************************************************/
18755163Sshin
18855163SshinVCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance)
18955163Sshin{
19062607Sitojun	VCHIQ_STATUS_T status;
19155163Sshin	VCHIQ_STATE_T *state = instance->state;
19255163Sshin
19355163Sshin	vchiq_log_trace(vchiq_core_log_level,
19455163Sshin		"%s(%p) called", __func__, instance);
19555163Sshin
19655163Sshin	if (lmutex_lock_interruptible(&state->mutex) != 0) {
19755163Sshin		vchiq_log_trace(vchiq_core_log_level,
19855163Sshin			"%s: call to lmutex_lock failed", __func__);
19955163Sshin		status = VCHIQ_RETRY;
20055163Sshin		goto failed;
20155163Sshin	}
20255163Sshin	status = vchiq_connect_internal(state, instance);
20355163Sshin
20455163Sshin	if (status == VCHIQ_SUCCESS)
20555163Sshin		instance->connected = 1;
20655163Sshin
20755163Sshin	lmutex_unlock(&state->mutex);
20855163Sshin
20955163Sshinfailed:
21055163Sshin	vchiq_log_trace(vchiq_core_log_level,
21155163Sshin		"%s(%p): returning %d", __func__, instance, status);
21278064Sume
21378064Sume	return status;
21478064Sume}
21578064SumeEXPORT_SYMBOL(vchiq_connect);
21662607Sitojun
21762607Sitojun/****************************************************************************
21862607Sitojun*
21962607Sitojun*   vchiq_add_service
22062607Sitojun*
22155163Sshin***************************************************************************/
22255163Sshin
22378064SumeVCHIQ_STATUS_T vchiq_add_service(
22478064Sume	VCHIQ_INSTANCE_T              instance,
22555163Sshin	const VCHIQ_SERVICE_PARAMS_T *params,
22655163Sshin	VCHIQ_SERVICE_HANDLE_T       *phandle)
22778064Sume{
22855163Sshin	VCHIQ_STATUS_T status;
22955163Sshin	VCHIQ_STATE_T *state = instance->state;
23055163Sshin	VCHIQ_SERVICE_T *service = NULL;
23162607Sitojun	int srvstate;
23255163Sshin
23355163Sshin	vchiq_log_trace(vchiq_core_log_level,
23455163Sshin		"%s(%p) called", __func__, instance);
23555163Sshin
23655163Sshin	*phandle = VCHIQ_SERVICE_HANDLE_INVALID;
23755163Sshin
23855163Sshin	srvstate = vchiq_is_connected(instance)
23978064Sume		? VCHIQ_SRVSTATE_LISTENING
24078064Sume		: VCHIQ_SRVSTATE_HIDDEN;
24178064Sume
24278064Sume	service = vchiq_add_service_internal(
24362607Sitojun		state,
24455163Sshin		params,
24555163Sshin		srvstate,
24655163Sshin		instance,
24755163Sshin		NULL);
24855163Sshin
24955163Sshin	if (service) {
25055163Sshin		*phandle = service->handle;
25178064Sume		status = VCHIQ_SUCCESS;
25278064Sume	} else
25378064Sume		status = VCHIQ_ERROR;
25455163Sshin
25555163Sshin	vchiq_log_trace(vchiq_core_log_level,
25678064Sume		"%s(%p): returning %d", __func__, instance, status);
25755163Sshin
25855163Sshin	return status;
25955163Sshin}
26055163SshinEXPORT_SYMBOL(vchiq_add_service);
26155163Sshin
26255163Sshin/****************************************************************************
26355163Sshin*
26455163Sshin*   vchiq_open_service
26555163Sshin*
26655163Sshin***************************************************************************/
26755163Sshin
26855163SshinVCHIQ_STATUS_T vchiq_open_service(
26978064Sume	VCHIQ_INSTANCE_T              instance,
27055163Sshin	const VCHIQ_SERVICE_PARAMS_T *params,
27155163Sshin	VCHIQ_SERVICE_HANDLE_T       *phandle)
27266807Skris{
27366807Skris	VCHIQ_STATUS_T   status = VCHIQ_ERROR;
27466807Skris	VCHIQ_STATE_T   *state = instance->state;
27566807Skris	VCHIQ_SERVICE_T *service = NULL;
27666807Skris
27766807Skris	vchiq_log_trace(vchiq_core_log_level,
27855163Sshin		"%s(%p) called", __func__, instance);
27955163Sshin
28055163Sshin	*phandle = VCHIQ_SERVICE_HANDLE_INVALID;
28155163Sshin
28255163Sshin	if (!vchiq_is_connected(instance))
28355163Sshin		goto failed;
28455163Sshin
28555163Sshin	service = vchiq_add_service_internal(state,
28655163Sshin		params,
28755163Sshin		VCHIQ_SRVSTATE_OPENING,
28855163Sshin		instance,
28955163Sshin		NULL);
29055163Sshin
29155163Sshin	if (service) {
29255163Sshin		*phandle = service->handle;
29355163Sshin		status = vchiq_open_service_internal(service,
29455163Sshin		    (uintptr_t)current);
29555163Sshin		if (status != VCHIQ_SUCCESS) {
29662607Sitojun			vchiq_remove_service(service->handle);
29755163Sshin			*phandle = VCHIQ_SERVICE_HANDLE_INVALID;
29855163Sshin		}
29955163Sshin	}
30055163Sshin
30155163Sshinfailed:
30255163Sshin	vchiq_log_trace(vchiq_core_log_level,
30355163Sshin		"%s(%p): returning %d", __func__, instance, status);
30455163Sshin
30578064Sume	return status;
30655163Sshin}
30755163SshinEXPORT_SYMBOL(vchiq_open_service);
30855163Sshin
30955163SshinVCHIQ_STATUS_T
31055163Sshinvchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
31155163Sshin	void *data, unsigned int size, void *userdata)
31262607Sitojun{
31355163Sshin	return vchiq_bulk_transfer(handle,
31462607Sitojun		VCHI_MEM_HANDLE_INVALID, data, size, userdata,
31562607Sitojun		VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
31655163Sshin}
31755163SshinEXPORT_SYMBOL(vchiq_queue_bulk_transmit);
31855163Sshin
31955163SshinVCHIQ_STATUS_T
32062607Sitojunvchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
32162607Sitojun	unsigned int size, void *userdata)
32262607Sitojun{
32355163Sshin	return vchiq_bulk_transfer(handle,
32455163Sshin		VCHI_MEM_HANDLE_INVALID, data, size, userdata,
32555163Sshin		VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);
32655163Sshin}
32755163SshinEXPORT_SYMBOL(vchiq_queue_bulk_receive);
32862607Sitojun
32955163SshinVCHIQ_STATUS_T
33062607Sitojunvchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, void *data,
33162607Sitojun	unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
33255163Sshin{
33362607Sitojun	VCHIQ_STATUS_T status;
33462607Sitojun
33562607Sitojun	switch (mode) {
33662607Sitojun	case VCHIQ_BULK_MODE_NOCALLBACK:
33762607Sitojun	case VCHIQ_BULK_MODE_CALLBACK:
33862607Sitojun		status = vchiq_bulk_transfer(handle,
33962607Sitojun			VCHI_MEM_HANDLE_INVALID, data, size, userdata,
34062607Sitojun			mode, VCHIQ_BULK_TRANSMIT);
34162607Sitojun		break;
34262607Sitojun	case VCHIQ_BULK_MODE_BLOCKING:
34355163Sshin		status = vchiq_blocking_bulk_transfer(handle,
34455163Sshin			data, size, VCHIQ_BULK_TRANSMIT);
34555163Sshin		break;
34662607Sitojun	default:
34755163Sshin		return VCHIQ_ERROR;
34855163Sshin	}
34955163Sshin
35055163Sshin	return status;
35178064Sume}
35255163SshinEXPORT_SYMBOL(vchiq_bulk_transmit);
35378064Sume
35478064SumeVCHIQ_STATUS_T
35555163Sshinvchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
35655163Sshin	unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
35755163Sshin{
35855163Sshin	VCHIQ_STATUS_T status;
35955163Sshin
360119037Sume	switch (mode) {
361119037Sume	case VCHIQ_BULK_MODE_NOCALLBACK:
362119037Sume	case VCHIQ_BULK_MODE_CALLBACK:
363119037Sume		status = vchiq_bulk_transfer(handle,
364119037Sume			VCHI_MEM_HANDLE_INVALID, data, size, userdata,
365119037Sume			mode, VCHIQ_BULK_RECEIVE);
366119037Sume		break;
367119037Sume	case VCHIQ_BULK_MODE_BLOCKING:
36855163Sshin		status = vchiq_blocking_bulk_transfer(handle,
36955163Sshin			data, size, VCHIQ_BULK_RECEIVE);
37078064Sume		break;
37178064Sume	default:
37278064Sume		return VCHIQ_ERROR;
37378064Sume	}
37478064Sume
37578064Sume	return status;
37678064Sume}
37778064SumeEXPORT_SYMBOL(vchiq_bulk_receive);
37878064Sume
37955163Sshinstatic VCHIQ_STATUS_T
38055163Sshinvchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
38155163Sshin	unsigned int size, VCHIQ_BULK_DIR_T dir)
38255163Sshin{
38355163Sshin	VCHIQ_INSTANCE_T instance;
38455163Sshin	VCHIQ_SERVICE_T *service;
38555163Sshin	VCHIQ_STATUS_T status;
38655163Sshin	struct bulk_waiter_node *waiter = NULL;
38755163Sshin	struct list_head *pos;
38855163Sshin
38955163Sshin	service = find_service_by_handle(handle);
39055163Sshin	if (!service)
39178064Sume		return VCHIQ_ERROR;
39255163Sshin
39378064Sume	instance = service->instance;
39478064Sume
395110666Sache	unlock_service(service);
396110666Sache
397110666Sache	lmutex_lock(&instance->bulk_waiter_list_mutex);
398110666Sache	list_for_each(pos, &instance->bulk_waiter_list) {
399110666Sache		if (list_entry(pos, struct bulk_waiter_node,
40055163Sshin				list)->pid == current->p_pid) {
40155163Sshin			waiter = list_entry(pos,
40255163Sshin				struct bulk_waiter_node,
40355163Sshin				list);
40455163Sshin			list_del(pos);
40555163Sshin			break;
40655163Sshin		}
40755163Sshin	}
40855163Sshin	lmutex_unlock(&instance->bulk_waiter_list_mutex);
40955163Sshin
41055163Sshin	if (waiter) {
41155163Sshin		VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
41255163Sshin		if (bulk) {
41378064Sume			/* This thread has an outstanding bulk transfer. */
41455163Sshin			if ((bulk->data != data) ||
41578064Sume				(bulk->size != size)) {
41678064Sume				/* This is not a retry of the previous one.
41762607Sitojun				** Cancel the signal when the transfer
41855163Sshin				** completes. */
41955163Sshin				spin_lock(&bulk_waiter_spinlock);
42055163Sshin				bulk->userdata = NULL;
42155163Sshin				spin_unlock(&bulk_waiter_spinlock);
42255163Sshin			}
42378064Sume		}
42478064Sume	}
42578064Sume
42678064Sume	if (!waiter) {
42778064Sume		waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);
42878064Sume		if (!waiter) {
42978064Sume			vchiq_log_error(vchiq_core_log_level,
43078064Sume				"%s - out of memory", __func__);
43178064Sume			return VCHIQ_ERROR;
43255163Sshin		}
43355163Sshin	}
43455163Sshin
43555163Sshin	status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID,
43655163Sshin		data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING,
43755163Sshin		dir);
43855163Sshin	if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
43955163Sshin		!waiter->bulk_waiter.bulk) {
440119041Sume		VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
441119041Sume		if (bulk) {
44255163Sshin			/* Cancel the signal when the transfer
44355163Sshin			 ** completes. */
44455163Sshin			spin_lock(&bulk_waiter_spinlock);
44555163Sshin			bulk->userdata = NULL;
44655163Sshin			spin_unlock(&bulk_waiter_spinlock);
44755163Sshin		}
44855163Sshin		_sema_destroy(&waiter->bulk_waiter.event);
44955163Sshin
45078064Sume		kfree(waiter);
45178064Sume	} else {
45278064Sume		waiter->pid = current->p_pid;
45378064Sume		lmutex_lock(&instance->bulk_waiter_list_mutex);
45478064Sume		list_add(&waiter->list, &instance->bulk_waiter_list);
45578064Sume		lmutex_unlock(&instance->bulk_waiter_list_mutex);
45678064Sume		vchiq_log_info(vchiq_arm_log_level,
45778064Sume				"saved bulk_waiter %x for pid %d",
45878064Sume				(unsigned int)waiter, current->p_pid);
45978064Sume	}
46078064Sume
46178064Sume	return status;
46278064Sume}
46378064Sume