fsl_dsp_proxy.c 20.6 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
//
// DSP proxy driver transfers messages between DSP driver and DSP framework
//
// Copyright 2018 NXP
// Copyright (C) 2017 Cadence Design Systems, Inc.

#include "fsl_dsp_proxy.h"
#include "fsl_dsp.h"

/* ...initialize message queue */
void xf_msg_queue_init(struct xf_msg_queue *queue)
{
	queue->head = queue->tail = NULL;
}

/* ...get message queue head */
struct xf_message *xf_msg_queue_head(struct xf_msg_queue *queue)
{
	return queue->head;
}

/* ...allocate new message from the pool */
struct xf_message *xf_msg_alloc(struct xf_proxy *proxy)
{
	struct xf_message *m = proxy->free;

	/* ...make sure we have a free message item */
	if (m != NULL) {
		/* ...get message from the pool */
		proxy->free = m->next, m->next = NULL;
	}

	return m;
}

/* ...return message to the pool of free items */
void xf_msg_free(struct xf_proxy *proxy, struct xf_message *m)
{
	/* ...put message into the head of free items list */
	m->next = proxy->free, proxy->free = m;

	/* ...notify potential client waiting for message */
	wake_up(&proxy->busy);
}

/* ...return all messages from the queue to the pool of free items */
void xf_msg_free_all(struct xf_proxy *proxy, struct xf_msg_queue *queue)
{
	struct xf_message *m = queue->head;

	/* ...check if there is anything in the queue */
	if (m != NULL) {
		queue->tail->next = proxy->free;
		proxy->free = queue->head;
		queue->head = queue->tail = NULL;

		/* ...notify potential client waiting for message */
		wake_up(&proxy->busy);
	}
}

/* ...submit message to a queue */
int xf_msg_enqueue(struct xf_msg_queue *queue, struct xf_message *m)
{
	int first = (queue->head == NULL);

	/* ...set pointer to next item */
	m->next = NULL;

	/* ...advance head/tail pointer as required */
	if (first)
		queue->head = m;
	else
		queue->tail->next = m;

	/* ...new tail points to this message */
	queue->tail = m;

	return first;
}

/* ...retrieve next message from the per-task queue */
struct xf_message *xf_msg_dequeue(struct xf_msg_queue *queue)
{
	struct xf_message *m = queue->head;

	/* ...check if there is anything in the queue */
	if (m != NULL) {
		/* ...pop message from the head of the list */
		queue->head = m->next;
		if (queue->head == NULL)
			queue->tail = NULL;
	}

	return m;
}

/* ...helper function for requesting execution message from a pool */
struct xf_message *xf_msg_available(struct xf_proxy *proxy)
{
	struct xf_message *m;

	/* ...acquire global lock */
	xf_lock(&proxy->lock);

	/* ...try to allocate the message */
	m = xf_msg_alloc(proxy);
	if (m  == NULL) {
		/* ...failed to allocate message; release lock */
		xf_unlock(&proxy->lock);
	}

	/* ...if successfully allocated */
	return m;
}

/* ...helper function for receiving a message from per-client queue */
struct xf_message *xf_msg_received(struct xf_proxy *proxy,
				struct xf_msg_queue *queue)
{
	struct xf_message *m;

	/* ...acquire global lock */
	xf_lock(&proxy->lock);

	/* ...try to peek message from the queue */
	m = xf_msg_dequeue(queue);
	if (m == NULL) {
		/* ...queue is empty; release lock */
		xf_unlock(&proxy->lock);
	}

	/* ...if message is non-null, lock is held */
	return m;
}

/*
 * MU related functions
 */
u32 icm_intr_send(struct xf_proxy *proxy, u32 msg)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);

	MU_SendMessage(dsp_priv->mu_base_virtaddr, 0, msg);
	return 0;
}

int icm_intr_extended_send(struct xf_proxy *proxy,
				u32 msg,
				struct dsp_ext_msg *ext_msg)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);
	struct device *dev = dsp_priv->dev;
	union icm_header_t msghdr;

	msghdr.allbits = msg;
	if (msghdr.size != 8)
		dev_err(dev, "too much ext msg\n");

	MU_SendMessage(dsp_priv->mu_base_virtaddr, 1, ext_msg->phys);
	MU_SendMessage(dsp_priv->mu_base_virtaddr, 2, ext_msg->size);
	MU_SendMessage(dsp_priv->mu_base_virtaddr, 0, msg);

	return 0;
}

int send_dpu_ext_msg_addr(struct xf_proxy *proxy)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);
	union icm_header_t msghdr;
	struct dsp_ext_msg ext_msg;
	struct dsp_mem_msg *dpu_ext_msg =
	 (struct dsp_mem_msg *)((unsigned char *)dsp_priv->msg_buf_virt
					+ (MSG_BUF_SIZE / 2));
	int ret_val = 0;

	msghdr.allbits = 0;	/* clear all bits; */
	msghdr.ack  = 0;
	msghdr.intr = 1;
	msghdr.msg  = ICM_CORE_INIT;
	msghdr.size = 8;
	ext_msg.phys = dsp_priv->msg_buf_phys + (MSG_BUF_SIZE / 2);
	ext_msg.size = sizeof(struct dsp_mem_msg);

	dpu_ext_msg->ext_msg_phys = dsp_priv->msg_buf_phys;
	dpu_ext_msg->ext_msg_size = MSG_BUF_SIZE;
	dpu_ext_msg->scratch_phys =  dsp_priv->scratch_buf_phys;
	dpu_ext_msg->scratch_size =  dsp_priv->scratch_buf_size;
	dpu_ext_msg->dsp_config_phys =  dsp_priv->dsp_config_phys;
	dpu_ext_msg->dsp_config_size =  dsp_priv->dsp_config_size;
	dpu_ext_msg->dsp_board_type = dsp_priv->dsp_board_type;

	icm_intr_extended_send(proxy, msghdr.allbits, &ext_msg);

	return ret_val;
}

long icm_ack_wait(struct xf_proxy *proxy, u32 msg)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);
	struct device *dev = dsp_priv->dev;
	union icm_header_t msghdr;
	int err;

	msghdr.allbits = msg;
	/* wait response from mu */
	err = wait_for_completion_timeout(&proxy->cmd_complete,
				msecs_to_jiffies(1000));
	if (!err) {
		dev_err(dev, "icm ack timeout! %x\n", msg);
		return -ETIMEDOUT;
	}

	dev_dbg(dev, "Ack recd for message 0x%08x\n", msghdr.allbits);

	return 0;
}

irqreturn_t fsl_dsp_mu_isr(int irq, void *dev_id)
{
	struct xf_proxy *proxy = dev_id;
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);
	struct device *dev = dsp_priv->dev;
	union icm_header_t msghdr;
	u32 reg;

	MU_ReceiveMsg(dsp_priv->mu_base_virtaddr, 0, &reg);
	msghdr = (union icm_header_t)reg;

	if (msghdr.intr == 1) {
		dev_dbg(dev, "INTR: Received ICM intr, msg 0x%08x\n",
						msghdr.allbits);
		switch (msghdr.msg) {
		case ICM_CORE_EXIT:
			break;
		case ICM_CORE_READY:
			send_dpu_ext_msg_addr(proxy);
			proxy->is_ready = 1;
			complete(&proxy->cmd_complete);
			break;
		case XF_SUSPEND:
		case XF_RESUME:
			complete(&proxy->cmd_complete);
			break;
		default:
			schedule_work(&proxy->work);
			break;
		}
	} else if (msghdr.ack == 1) {
		dev_dbg(dev, "INTR: Received ICM ack 0x%08x\n", msghdr.size);
		msghdr.ack = 0;
	} else {
		dev_dbg(dev, "Received false ICM intr 0x%08x\n",
							msghdr.allbits);
	}

	return IRQ_HANDLED;
}

/*
 * Proxy related functions
 */
/* ...NULL-address specification */
#define XF_PROXY_NULL           (~0U)

#define XF_PROXY_BADADDR        (dsp_priv->scratch_buf_size)

/* ...shared memory translation - kernel virtual address to shared address */
u32 xf_proxy_b2a(struct xf_proxy *proxy, void *b)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);

	if (b == NULL)
		return XF_PROXY_NULL;
	else if ((u32)(b - dsp_priv->scratch_buf_virt) <
					dsp_priv->scratch_buf_size)
		return (u32)(b - dsp_priv->scratch_buf_virt);
	else
		return XF_PROXY_BADADDR;
}

/* ...shared memory translation - shared address to kernel virtual address */
void *xf_proxy_a2b(struct xf_proxy *proxy, u32 address)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);

	if (address < dsp_priv->scratch_buf_size)
		return dsp_priv->scratch_buf_virt + address;
	else if (address == XF_PROXY_NULL)
		return NULL;
	else
		return (void *) -1;
}

/* ...process association between response received and intended client */
static void xf_cmap(struct xf_proxy *proxy, struct xf_message *m)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);
	u32 id = XF_AP_IPC_CLIENT(m->id);
	struct xf_client *client;

	/* ...process messages addressed to proxy itself */
	if (id == 0) {
		/* ...place message into local response queue */
		xf_msg_enqueue(&proxy->response, m);
		wake_up(&proxy->wait);
		return;
	}

	/* ...make sure the client ID is sane */
	client = xf_client_lookup(dsp_priv, id);
	if (!client) {
		pr_err("rsp[id:%08x]: client lookup failed", m->id);
		xf_msg_free(proxy, m);
		return;
	}

	/* ...make sure client is bound to this proxy interface */
	if (client->proxy != proxy) {
		pr_err("rsp[id:%08x]: wrong proxy interface", m->id);
		xf_msg_free(proxy, m);
		return;
	}

	/* ...place message into local response queue */
	if (xf_msg_enqueue(&client->queue, m))
		wake_up(&client->wait);
}

/* ...retrieve pending responses from shared memory ring-buffer */
static u32 xf_shmem_process_responses(struct xf_proxy *proxy)
{
	struct xf_message *m;
	u32 read_idx, write_idx;
	int status;

	status = 0;

	/* ...get current values of read/write pointers in response queue */
	read_idx = XF_PROXY_READ(proxy, rsp_read_idx);
	write_idx = XF_PROXY_READ(proxy, rsp_write_idx);

	/* ...process all committed responses */
	while (!XF_QUEUE_EMPTY(read_idx, write_idx)) {
		struct xf_proxy_message *response;

		/* ...allocate execution message */
		m = xf_msg_alloc(proxy);
		if (m  == NULL)
			break;

		/* ...mark the interface status has changed */
		status |= (XF_QUEUE_FULL(read_idx, write_idx) ? 0x3 : 0x1);

		/* ...get oldest not yet processed response */
		response = XF_PROXY_RESPONSE(proxy, XF_QUEUE_IDX(read_idx));

		/* ...fill message parameters */
		m->id = response->session_id;
		m->opcode = response->opcode;
		m->length = response->length;
		m->buffer = xf_proxy_a2b(proxy, response->address);
		m->ret = response->ret;

		/* ...advance local reading index copy */
		read_idx = XF_QUEUE_ADVANCE_IDX(read_idx);

		/* ...update shadow copy of reading index */
		XF_PROXY_WRITE(proxy, rsp_read_idx, read_idx);

		/* ...submit message to proper client */
		xf_cmap(proxy, m);
	}

	return status;
}

/* ...put pending commands into shared memory ring-buffer */
static u32 xf_shmem_process_commands(struct xf_proxy *proxy)
{
	struct xf_message *m;
	u32 read_idx, write_idx;
	int status = 0;

	/* ...get current value of peer read pointer */
	write_idx = XF_PROXY_READ(proxy, cmd_write_idx);
	read_idx = XF_PROXY_READ(proxy, cmd_read_idx);

	/* ...submit any pending commands */
	while (!XF_QUEUE_FULL(read_idx, write_idx)) {
		struct xf_proxy_message *command;

		/* ...check if we have a pending command */
		m = xf_msg_dequeue(&proxy->command);
		if (m  == NULL)
			break;

		/* ...always mark the interface status has changed */
		status |= 0x3;

		/* ...select the place for the command */
		command = XF_PROXY_COMMAND(proxy, XF_QUEUE_IDX(write_idx));

		/* ...put the response message fields */
		command->session_id = m->id;
		command->opcode = m->opcode;
		command->length = m->length;
		command->address = xf_proxy_b2a(proxy, m->buffer);
		command->ret = m->ret;

		/* ...return message back to the pool */
		xf_msg_free(proxy, m);

		/* ...advance local writing index copy */
		write_idx = XF_QUEUE_ADVANCE_IDX(write_idx);

		/* ...update shared copy of queue write pointer */
		XF_PROXY_WRITE(proxy, cmd_write_idx, write_idx);
	}

	if (status)
		icm_intr_send(proxy, 0);

	return status;
}

/* ...shared memory interface maintenance routine */
void xf_proxy_process(struct work_struct *w)
{
	struct xf_proxy *proxy = container_of(w, struct xf_proxy, work);
	int status = 0;

	/* ...get exclusive access to internal data */
	xf_lock(&proxy->lock);

	do {
		/* ...process outgoing commands first */
		status = xf_shmem_process_commands(proxy);

		/* ...process all pending responses */
		status |= xf_shmem_process_responses(proxy);

	} while (status);

	/* ...unlock internal proxy data */
	xf_unlock(&proxy->lock);
}

/* ...initialize shared memory interface */
int xf_proxy_init(struct xf_proxy *proxy)
{
	struct fsl_dsp *dsp_priv = container_of(proxy,
					struct fsl_dsp, proxy);
	struct xf_message *m;
	int i;

	/* ...create a list of all messages in a pool; set head pointer */
	proxy->free = &proxy->pool[0];

	/* ...put all messages into a single-linked list */
	for (i = 0, m = proxy->free; i < XF_CFG_MESSAGE_POOL_SIZE - 1; i++, m++)
		m->next = m + 1;

	/* ...set list tail pointer */
	m->next = NULL;

	/* ...initialize proxy lock */
	xf_lock_init(&proxy->lock);

	/* ...initialize proxy thread message queues */
	xf_msg_queue_init(&proxy->command);
	xf_msg_queue_init(&proxy->response);

	/* ...initialize global busy queue */
	init_waitqueue_head(&proxy->busy);
	init_waitqueue_head(&proxy->wait);

	/* ...create work structure */
	INIT_WORK(&proxy->work, xf_proxy_process);

	/* ...set pointer to shared memory */
	proxy->ipc.shmem = (struct xf_shmem_data *)dsp_priv->msg_buf_virt;

	/* ...initialize shared memory interface */
	XF_PROXY_WRITE(proxy, cmd_read_idx, 0);
	XF_PROXY_WRITE(proxy, cmd_write_idx, 0);
	XF_PROXY_WRITE(proxy, cmd_invalid, 0);
	XF_PROXY_WRITE(proxy, rsp_read_idx, 0);
	XF_PROXY_WRITE(proxy, rsp_write_idx, 0);
	XF_PROXY_WRITE(proxy, rsp_invalid, 0);

	return 0;
}

/* ...trigger shared memory interface processing */
void xf_proxy_notify(struct xf_proxy *proxy)
{
	schedule_work(&proxy->work);
}

/* ...submit a command to proxy pending queue (lock released upon return) */
void xf_proxy_command(struct xf_proxy *proxy, struct xf_message *m)
{
	int first;

	/* ...submit message to proxy thread */
	first = xf_msg_enqueue(&proxy->command, m);

	/* ...release the lock */
	xf_unlock(&proxy->lock);

	/* ...notify thread about command reception */
	(first ? xf_proxy_notify(proxy), 1 : 0);
}

/*
 * Proxy cmd send and receive functions
 */
int xf_cmd_send(struct xf_proxy *proxy,
				u32 id,
				u32 opcode,
				void *buffer,
				u32 length)
{
	struct xf_message *m;
	int ret;

	/* ...retrieve message handle (take the lock on success) */
	ret = wait_event_interruptible(proxy->busy,
					(m = xf_msg_available(proxy)) != NULL);
	if (ret)
		return -EINTR;

	/* ...fill-in message parameters (lock is taken) */
	m->id = id;
	m->opcode = opcode;
	m->length = length;
	m->buffer = buffer;
	m->ret = 0;

	/* ...submit command to the proxy */
	xf_proxy_command(proxy, m);

	return 0;
}

struct xf_message *xf_cmd_recv(struct xf_proxy *proxy,
						  wait_queue_head_t *wq,
						  struct xf_msg_queue *queue,
						  int wait)
{
	struct xf_message *m = NULL;
	int ret;

	/* ...wait for message reception (take lock on success) */
	ret = wait_event_interruptible(*wq,
			(m = xf_msg_received(proxy, queue)) != NULL || !wait
			|| !proxy->is_active);
	if (ret)
		return ERR_PTR(-EINTR);

	/* ...return message with a lock taken */
	return m;
}

struct xf_message *xf_cmd_recv_timeout(struct xf_proxy *proxy,
				       wait_queue_head_t *wq,
				       struct xf_msg_queue *queue, int wait)
{
	struct xf_message *m;
	int ret;

	/* ...wait for message reception (take lock on success) */
	ret = wait_event_interruptible_timeout(*wq,
			(m = xf_msg_received(proxy, queue)) != NULL || !wait,
			msecs_to_jiffies(1000));
	if (ret < 0)
		return ERR_PTR(-EINTR);

	if (ret == 0)
		return ERR_PTR(-ETIMEDOUT);

	/* ...return message with a lock taken */
	return m;
}

/* ...helper function for synchronous command execution */
struct xf_message *xf_cmd_send_recv(struct xf_proxy *proxy,
							   u32 id, u32 opcode,
							   void *buffer,
							   u32 length)
{
	int ret;

	/* ...send command to remote proxy */
	ret = xf_cmd_send(proxy, id, opcode, buffer, length);
	if (ret)
		return ERR_PTR(ret);

	/* ...wait for message delivery */
	return xf_cmd_recv(proxy, &proxy->wait, &proxy->response, 1);
}

struct xf_message *xf_cmd_send_recv_wq(struct xf_proxy *proxy, u32 id,
				       u32 opcode, void *buffer, u32 length,
				       wait_queue_head_t *wq,
				       struct xf_msg_queue *queue)
{
	int ret;

	/* ...send command to remote proxy */
	ret = xf_cmd_send(proxy, id, opcode, buffer, length);
	if (ret)
		return ERR_PTR(ret);

	/* ...wait for message delivery */
	return xf_cmd_recv(proxy, wq, queue, 1);
}

struct xf_message *xf_cmd_send_recv_complete(struct xf_client *client,
					     struct xf_proxy *proxy,
					     u32 id, u32 opcode, void *buffer,
					     u32 length,
					     struct work_struct *work,
					     struct completion *completion)
{
	struct xf_message *m;
	int ret;

	/* ...retrieve message handle (take the lock on success) */
	m = xf_msg_available(proxy);
	if (!m)
		return ERR_PTR(-EBUSY);

	/* ...fill-in message parameters (lock is taken) */
	m->id = id;
	m->opcode = opcode;
	m->length = length;
	m->buffer = buffer;
	m->ret = 0;

	init_completion(completion);

	/* ...submit command to the proxy */
	xf_proxy_command(proxy, m);

	schedule_work(work);

	/* ...wait for message reception (take lock on success) */
	ret = wait_for_completion_timeout(completion,
					  msecs_to_jiffies(1000));
	if (!ret)
		return ERR_PTR(-ETIMEDOUT);

	m = &client->m;

	/* ...return message with a lock taken */
	return m;
}
/*
 * Proxy allocate and free memory functions
 */
/* ...allocate memory buffer for kernel use */
int xf_cmd_alloc(struct xf_proxy *proxy, void **buffer, u32 length)
{
	struct xf_message *m;
	u32 id = 0;
	int ret;

	/* ...send command to remote proxy */
	m = xf_cmd_send_recv(proxy, id, XF_ALLOC, NULL, length);
	if (IS_ERR(m)) {
		xf_unlock(&proxy->lock);
		ret = PTR_ERR(m);
		return ret;
	}

	/* ...check if response is expected */
	if (m->opcode == XF_ALLOC && m->buffer != NULL) {
		*buffer = m->buffer;
		ret = 0;
	} else {
		ret = -ENOMEM;
	}

	/* ...free message and release proxy lock */
	xf_msg_free(proxy, m);
	xf_unlock(&proxy->lock);

	return ret;
}

/* ...free memory buffer */
int xf_cmd_free(struct xf_proxy *proxy, void *buffer, u32 length)
{
	struct xf_message *m;
	u32 id = 0;
	int ret;

	/* ...synchronously execute freeing command */
	m = xf_cmd_send_recv(proxy, id, XF_FREE, buffer, length);
	if (IS_ERR(m)) {
		xf_unlock(&proxy->lock);
		ret = PTR_ERR(m);
		return ret;
	}

	/* ...check if response is expected */
	if (m->opcode == XF_FREE)
		ret = 0;
	else
		ret = -EINVAL;

	/* ...free message and release proxy lock */
	xf_msg_free(proxy, m);
	xf_unlock(&proxy->lock);

	return ret;
}

/*
 * suspend & resume functions
 */
int xf_cmd_send_suspend(struct xf_proxy *proxy)
{
	union icm_header_t msghdr;
	int ret = 0;

	init_completion(&proxy->cmd_complete);

	msghdr.allbits = 0;	/* clear all bits; */
	msghdr.ack  = 0;
	msghdr.intr = 1;
	msghdr.msg  = XF_SUSPEND;
	msghdr.size = 0;
	icm_intr_send(proxy, msghdr.allbits);

	/* wait for response here */
	ret = icm_ack_wait(proxy, msghdr.allbits);

	return ret;
}

int xf_cmd_send_resume(struct xf_proxy *proxy)
{
	union icm_header_t msghdr;
	int ret = 0;

	init_completion(&proxy->cmd_complete);

	msghdr.allbits = 0;	/* clear all bits; */
	msghdr.ack  = 0;
	msghdr.intr = 1;
	msghdr.msg  = XF_RESUME;
	msghdr.size = 0;
	icm_intr_send(proxy, msghdr.allbits);

	/* wait for response here */
	ret = icm_ack_wait(proxy, msghdr.allbits);

	return ret;
}

/* ...open component handle */
int xf_open(struct xf_client *client, struct xf_proxy *proxy,
	    struct xf_handle *handle, const char *id, u32 core,
	    xf_response_cb response)
{
	void *b;
	struct xf_message   msg;
	struct xf_message   *rmsg;

	/* ...retrieve auxiliary control buffer from proxy - need I */
	handle->aux = xf_buffer_get(proxy->aux);

	b = xf_handle_aux(handle);

	msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0));
	msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
	msg.opcode = XF_REGISTER;
	msg.buffer = b;
	msg.length = strlen(id) + 1;
	msg.ret = 0;

	/* ...copy component identifier */
	memcpy(b, (void *)id, xf_buffer_length(handle->aux));

	/* ...execute command synchronously */
	rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
					 msg.buffer, msg.length, &client->work,
					 &client->compr_complete);

	if (IS_ERR(rmsg)) {
		xf_buffer_put(handle->aux), handle->aux = NULL;
		return PTR_ERR(rmsg);
	}
	/* ...save received component global client-id */
	handle->id = XF_MSG_SRC(rmsg->id);
	/* TODO: review cleanup */
	/* xf_msg_free(proxy, rmsg);
	 * xf_unlock(&proxy->lock); */

	/* ...if failed, release buffer handle */
	/* ...operation completed successfully; assign handle data */
	handle->response = response;
	handle->proxy = proxy;

	return 0;
}

/* ...close component handle */
int xf_close(struct xf_client *client, struct xf_handle *handle)
{
	struct xf_proxy *proxy = handle->proxy;
	struct xf_message   msg;
	struct xf_message *rmsg;

	/* ...do I need to take component lock here? guess no - tbd */

	/* ...buffers and stuff? - tbd */

	/* ...acquire global proxy lock */
	/* ...unregister component from remote DSP proxy (ignore result code) */

	msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), handle->id);
	msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
	msg.opcode = XF_UNREGISTER;
	msg.buffer = NULL;
	msg.length = 0;
	msg.ret = 0;

	/* ...execute command synchronously */
	rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
					 msg.buffer, msg.length, &client->work,
					 &client->compr_complete);

	if (IS_ERR(rmsg)) {
		xf_buffer_put(handle->aux), handle->aux = NULL;
		return PTR_ERR(rmsg);
	}
	/* TODO: review cleanup */
	/* xf_msg_free(proxy, rmsg);
	 * xf_unlock(&proxy->lock); */

	/* ...wipe out proxy pointer */
	handle->proxy = NULL;

	return 0;
}