summaryrefslogtreecommitdiffstats
path: root/app/ContactsView.qml
diff options
context:
space:
mode:
Diffstat (limited to 'app/ContactsView.qml')
-rw-r--r--app/ContactsView.qml102
1 files changed, 63 insertions, 39 deletions
diff --git a/app/ContactsView.qml b/app/ContactsView.qml
index 539a9e8..5c2c327 100644
--- a/app/ContactsView.qml
+++ b/app/ContactsView.qml
@@ -18,6 +18,7 @@
import QtQuick 2.6
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0
+import QtQuick.Controls.Styles 1.4
import AGL.Demo.Controls 1.0
import PhoneNumber 1.0
@@ -42,53 +43,76 @@ Item {
signal call(var cname, var cnumber)
- ListView {
+ ColumnLayout {
anchors.fill: parent
- model: ContactsModel
- //cacheBuffer: 2880
- delegate: MouseArea {
- width: ListView.view.width
- height: width / 3
- RowLayout {
- anchors.fill: parent
- anchors.leftMargin: 200
- spacing: 100
- Image {
- source: model.modelData.photo ? model.modelData.photo : './images/HMI_ContactScreen_ImageHolder-01.svg'
- Layout.preferredWidth: 160
- Layout.preferredHeight: 160
- }
- ColumnLayout {
- Label {
- Layout.fillWidth: true
- color: '#59FF7F'
- font.pixelSize: 50
- text: model.modelData.name
+ anchors.topMargin: 50
+ anchors.bottomMargin: 50
+ spacing: 20
+
+ Button {
+ Layout.preferredWidth: 600
+ Layout.preferredHeight: 75
+ Layout.alignment: Qt.AlignHCenter
+ id: control
+ text: "Sync Contacts"
+ font.pixelSize: 50
+ z: 1
+
+ onClicked: {
+ pbap.importContacts(-1)
+ }
+ }
+
+ ListView {
+ model: ContactsModel
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ //cacheBuffer: 2880
+ delegate: MouseArea {
+ width: ListView.view.width
+ height: width / 3
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: 200
+ spacing: 100
+ Image {
+ source: model.modelData.photo ? model.modelData.photo : './images/HMI_ContactScreen_ImageHolder-01.svg'
+ Layout.preferredWidth: 160
+ Layout.preferredHeight: 160
}
- Repeater {
- model: numbers
- delegate: Label {
+ ColumnLayout {
+ Label {
Layout.fillWidth: true
+ color: '#59FF7F'
font.pixelSize: 50
- text: display_type(type) + ": " + number
+ text: model.modelData.name
+ }
+ Repeater {
+ model: numbers
+ delegate: Label {
+ Layout.fillWidth: true
+ font.pixelSize: 50
+ text: display_type(type) + ": " + number
+ }
}
}
}
+ onClicked: {
+ root.call(model.name, model.numbers[0].number)
+ }
}
- onClicked: {
- root.call(model.name, model.numbers[0].number)
- }
- }
- section.property: 'name'
- section.criteria: ViewSection.FirstCharacter
- section.delegate: Item {
- Label {
- width: root.width / 5
- height: width
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- color: '#59FF7F'
- text: section
+ section.property: 'name'
+ section.criteria: ViewSection.FirstCharacter
+ section.delegate: Item {
+ Label {
+ width: root.width / 5
+ height: width
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: '#59FF7F'
+ text: section
+ }
}
}
}
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
// SPDX-License-Identifier: GPL-2.0
/*
 * AVIRT - ALSA Virtual Soundcard
 *
 * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd
 *
 * core.c - AVIRT core internals
 */

#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <sound/initval.h>
#include <linux/platform_device.h>

#include "core.h"

MODULE_AUTHOR("James O'Shannessy <james.oshannessy@fiberdyne.com.au>");
MODULE_AUTHOR("Mark Farrugia <mark.farrugia@fiberdyne.com.au>");
MODULE_DESCRIPTION("ALSA virtual, dynamic soundcard");
MODULE_LICENSE("GPL v2");

#define D_LOGNAME "core"

#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args)
#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args)
#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args)

#define PCM_OPS_SET(pcm_ops_ap, pcm_ops, cb)                                   \
	((pcm_ops_ap->cb) ? ((*pcm_ops)->cb = pcm_ops_ap->cb) :                \
			    ((*pcm_ops)->cb = NULL));

#define SND_AVIRT_DRIVER "snd_avirt"

static struct snd_avirt_core core = {
	.version = { 0, 0, 1 },
	.stream_count = 0,
	.streams_configured = false,
};

static LIST_HEAD(audiopath_list);

/**
 * pcm_private_free - callback function to free private data allocated to pcm
 * @pcm: the PCM object
 */
static void pcm_private_free(struct snd_pcm *pcm)
{
	struct snd_avirt_private_data *avirt_private_data;

	/* Free Audio Path private data */
	avirt_private_data = (struct snd_avirt_private_data *)pcm->private_data;
	if (avirt_private_data) {
		if (avirt_private_data->ap_private_data[0] &&
		    avirt_private_data->ap_private_free)
			avirt_private_data->ap_private_free(pcm);
	}

	kfree(pcm->private_data);
}

static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream)
{
	struct snd_avirt_private_data *avirt_private_data;
	struct snd_pcm *pcm;
	bool playback = false, capture = false;
	int err;

	if (!stream->direction)
		playback = true;
	else
		capture = true;

	/** Special case: loopback */
	if (!strcmp(stream->map, "ap_loopback")) {
		playback = true;
		capture = true;
	}

	if (stream->internal) {
		err = snd_pcm_new_internal(core.card, stream->name,
					   stream->device, playback, capture,
					   &pcm);
	} else {
		err = snd_pcm_new(core.card, stream->name, stream->device,
				  playback, capture, &pcm);
	}

	if (err < 0) {
		D_ERRORK("Failed to create PCM device for stream: '%s'",
			 stream->name);
		return ERR_PTR(err);
	}

	/** Register driver callbacks */
	if (playback)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
				stream->pcm_ops);
	if (capture)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, stream->pcm_ops);

	pcm->info_flags = 0;
	strncpy(pcm->name, stream->name, MAX_NAME_LEN);

	avirt_private_data = kzalloc(sizeof(*avirt_private_data), GFP_KERNEL);
	pcm->private_data = avirt_private_data;
	// Set the private free function for the private user data
	pcm->private_free = pcm_private_free;

	snd_device_register(core.card, pcm);

	return pcm;
}

void snd_avirt_stream_try_destroy(struct snd_avirt_stream *stream)
{
	unsigned long _flags;
	struct snd_pcm_substream *substream =
		stream->pcm->streams[stream->direction].substream;

	snd_pcm_stream_lock_irqsave(substream, _flags);
	if (substream->runtime) {
		if (snd_pcm_running(substream)) {
			if (0 !=
			    snd_pcm_stop(substream, SNDRV_PCM_STATE_SUSPENDED))
				D_ERRORK("Could not stop PCM '%s'",
					 stream->name);
		}
	}
	snd_pcm_stream_unlock_irqrestore(substream, _flags);

	snd_device_free(core.card, stream->pcm);
	kfree(stream->pcm_ops);
	kfree(stream);

	core.stream_count--;
}

static struct snd_avirt_route *snd_avirt_route_get(const char *uid)
{
	struct list_head *entry;
	struct config_item *item;
	struct snd_avirt_route *route;

	list_for_each (entry, &core.route_group->cg_children) {
		item = container_of(entry, struct config_item, ci_entry);
		route = snd_avirt_route_from_config_item(item);
		if (!strcmp(route->uid, uid))
			return route;
	}

	return NULL;
}

/**
 * int snd_avirt_route_try_complete - Set up remaining parameters for a route.
 *                                    Channels, sink, and source Audio Paths
 *                                    should be set when calling this function.
 * @stream: The route to attempt to finalize parameters for.
 * @return: 0 on success, negative ERRNO on failure
 */
int snd_avirt_route_try_complete(struct snd_avirt_route *route)
{
	return 0;
}

/**
 * int snd_avirt_stream_try_complete - Set up remaining parameters for a stream.
 *                                     Channels and map should be set when
 *                                     calling this function.
 * @stream: The stream to attempt to finalize parameters for.
 * @return: 0 on success, negative ERRNO on failure
 */
int snd_avirt_stream_try_complete(struct snd_avirt_stream *stream)
{
	struct snd_avirt_audiopath *audiopath;
	struct snd_avirt_route *route;
	struct snd_pcm_ops *pcm_ops_ap;

	if (snd_avirt_streams_configured())
		return -EPERM;

	if ((stream->channels == 0) || (!strcmp(stream->map, "none")))
		return -EPERM;

	audiopath = snd_avirt_audiopath_get(stream->map);
	if (!audiopath) {
		D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map);
	}

	/* Check for any routes that have been created for this stream */
	route = snd_avirt_route_get(stream->name);
	if (route) {
		if (audiopath == route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE])
			route->endpoint_stream[SND_AVIRT_ROUTE_SOURCE] = stream;
		else if (audiopath == route->endpoint_ap[SND_AVIRT_ROUTE_SINK])
			route->endpoint_stream[SND_AVIRT_ROUTE_SINK] = stream;
		else {
			D_INFOK("Cannot set route. Audio Path not compatible");
			return -EPERM;
		}

		stream->route = route;
	}

	/* Set up PCM ops */
	if (!stream->direction)
		pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops;
	else
		pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops;

	if (!pcm_ops_ap) {
		D_ERRORK("No PCM ops for direction '%s' for Audio Path: %s",
			 (stream->direction) ? "capture" : "playback",
			 stream->map);
		return -EFAULT;
	}

	/* Set PCM ops for the Audio Path*/
	PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, pointer);
	PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, get_time_info);
	PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, fill_silence);
	PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, copy_user);
	PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, copy_kernel);
	PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, mmap);
	PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, ack);

	/* If not created, create the PCM device now */
	if (!stream->pcm) {
		stream->pcm = snd_avirt_pcm_create(stream);
		if (IS_ERR_OR_NULL(stream->pcm))
			return -EFAULT;
	}

	return 0;
}

/**
 * snd_avirt_streams_get - Get AVIRT streams for a given Audio Path map
 * @map: The Audio Path UID whose streams to find.
 * @stream_array: To be populated with streams.
 * @return: The number of streams found for the Audio Path.
 */
static int snd_avirt_streams_get(const char *map,
				 struct snd_avirt_stream_array *stream_array)
{
	struct list_head *entry;
	struct config_item *item;
	struct snd_avirt_stream *stream;

	list_for_each (entry, &core.stream_group->cg_children) {
		item = container_of(entry, struct config_item, ci_entry);
		stream = snd_avirt_stream_from_config_item(item);
		if (!strcmp(map, stream->map)) {
			stream_array->streams[stream_array->count++] = stream;
		}
	}

	return stream_array->count;
}

/**
 * snd_avirt_route_endpoint_pos - get route endpoint position for Audio Path
 * @pcm: The PCM whose route to inspect.
 * @ap_uid: The Audio Path UID to get
 * @endpoint: The route position (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK)
 *            of the Audio Path in it's route (if any).
 * @return: 0 if an Audio Path is found in the route, -1 if there is no route,
 *          or -2 otherwise.
 */
int snd_avirt_route_endpoint_pos(struct snd_pcm *pcm, const char *ap_uid,
				 snd_avirt_route_endpoint *endpoint)
{
	struct snd_avirt_audiopath *endpoint_ap;
	struct snd_avirt_stream *stream;
	int i;

	stream = snd_avirt_stream_find_by_device(pcm->device);
	if (IS_ERR_VALUE(stream) || !stream)
		goto exit_err;

	if (!stream->route)
		return -1;

	for (i = 0; i < 2; i++) {
		endpoint_ap = stream->route->endpoint_ap[i];
		if (endpoint_ap)
			if (!strcmp(endpoint_ap->uid, ap_uid)) {
				*endpoint = i;
				return 0;
			}
	}

exit_err:
	D_ERRORK("Could not find Audio Path '%s' in route '%s'", ap_uid,
		 stream->route->uid);
	return -2;
}

/**
 * snd_avirt_route_endpoint_copy - get endpoint copy function for a given
 *                                 Audio Path's source/sink.
 * @ap: The Audio Path whose endpoint copy function to find.
 * @endpoint: The endpoint (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK).
 * @return: A snd_pcm_copy_kernel function pointer that can be used to either:
 *          1. Source PCM data into the Audio Path, or,
 *          2. Sink PCM data out of the Audio Path.
 *          If no Audio Path endpoint is routed for 'endpoint', NULL is returned.
 */
snd_pcm_copy_kernel
snd_avirt_route_endpoint_copy(struct snd_pcm_substream *substream,
			      snd_avirt_route_endpoint endpoint)
{
	struct snd_avirt_audiopath *endpoint_ap;
	struct snd_avirt_stream *stream;

	if (endpoint < 0 || endpoint > 1) {
		D_ERRORK("Route endpoint must be 0 or 1");
		return NULL;
	}

	stream = snd_avirt_stream_find_by_device(substream->pcm->device);
	if (IS_ERR_VALUE(stream) || !stream)
		return NULL;

	if (!stream->route)
		return NULL;
	endpoint_ap = stream->route->endpoint_ap[endpoint];
	if (!endpoint_ap)
		return NULL;

	switch (endpoint) {
	case SND_AVIRT_ROUTE_SOURCE:
		return endpoint_ap->pcm_capture_ops->copy_kernel;
	case SND_AVIRT_ROUTE_SINK:
		return endpoint_ap->pcm_playback_ops->copy_kernel;
	}

	return NULL;
}
EXPORT_SYMBOL_GPL(snd_avirt_route_endpoint_copy);

/**
 * snd_avirt_route_endpoint_trigger - Trigger an Audio Path's endpoint
 *                                    (sink/source).
 * @uid: The Audio Path whose endpoint trigger function to call.
 * @endpoint: The endpoint (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK).
 * @return: 0 on success or -1 on failure.
 */
int snd_avirt_route_endpoint_trigger(struct snd_pcm_substream *substream,
				     snd_avirt_route_endpoint endpoint)
{
	struct snd_avirt_audiopath *endpoint_ap;
	struct snd_avirt_stream *stream;

	if (endpoint < 0 || endpoint > 1) {
		D_ERRORK("Route endpoint must be 0 or 1");
		return -1;
	}

	stream = snd_avirt_stream_find_by_device(substream->pcm->device);
	if (IS_ERR_VALUE(stream) || !stream)
		return -1;

	if (!stream->route)
		return -1;
	endpoint_ap = stream->route->endpoint_ap[endpoint];
	if (!endpoint_ap)
		return -1;

	endpoint_ap->pcm_exttrigger();

	return 0;
}
EXPORT_SYMBOL_GPL(snd_avirt_route_endpoint_trigger);

/**
 * snd_avirt_audiopath_get - get Audio Path by it's UID
 * @uid: The Audio Path UID to get
 * @return: The Audio Path if it exists, NULL otherwise.
 */
struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid)
{
	struct snd_avirt_audiopath_obj *ap_obj;

	list_for_each_entry (ap_obj, &audiopath_list, list) {
		if (!strcmp(ap_obj->path->uid, uid))
			return ap_obj->path;
	}

	return NULL;
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get);

/*
 * snd_avirt_audiopath_set_private_data - set PCM private data for an Audio Path
 * @ap: The Audio Path whose private data to set.
 * @pcm: The PCM where the private data is stored.
 * @ap_private_data: The value to set to the private data.
 * @return: 0 on success, -1 on failure.
 */
int snd_avirt_audiopath_set_private_data(struct snd_avirt_audiopath *ap,
					 struct snd_pcm *pcm,
					 void *ap_private_data)
{
	int err = 0;
	snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE;
	struct snd_avirt_stream *stream;
	struct snd_avirt_private_data *avirt_private_data;

	stream = snd_avirt_stream_find_by_device(pcm->device);
	if (IS_ERR_VALUE(stream) || !stream)
		goto exit_err;

	err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint);
	if (err == -2)
		goto exit_err;

	if (stream->internal && stream->route)
		pcm = stream->route->endpoint_stream[!endpoint]->pcm;

	avirt_private_data = pcm->private_data;
	if (!avirt_private_data)
		goto exit_err;

	avirt_private_data->ap_private_data[endpoint] = ap_private_data;

	return 0;

exit_err:
	D_ERRORK("Error setting private data for ap:%s, stream:%s, endpoint:%d",
		 ap->uid, pcm->name, endpoint);
	return -1;
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_set_private_data);

/*
 * snd_avirt_audiopath_get_private_data - get PCM private data for an Audio Path
 * @ap: The Audio Path whose private data to get.
 * @pcm: The PCM where the private data is stored.
 * @return: The value assigned to the private data.
 */
void *snd_avirt_audiopath_get_private_data(struct snd_avirt_audiopath *ap,
					   struct snd_pcm *pcm)
{
	int err = 0;
	snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE;
	struct snd_avirt_stream *stream;
	struct snd_avirt_private_data *avirt_private_data;

	stream = snd_avirt_stream_find_by_device(pcm->device);
	if (IS_ERR_VALUE(stream) || !stream)
		return NULL;

	err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint);
	if (err == -2)
		goto exit_err;

	if (stream->internal && stream->route)
		pcm = stream->route->endpoint_stream[!endpoint]->pcm;

	avirt_private_data = pcm->private_data;
	if (!avirt_private_data)
		goto exit_err;

	return avirt_private_data->ap_private_data[endpoint];

exit_err:
	D_ERRORK("Error getting private data for ap:%s, stream:%s, endpoint:%d",
		 ap->uid, pcm->name, endpoint);
	return NULL;
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get_private_data);

/**
 * snd_avirt_audiopath_register - register Audio Path with AVIRT
 * @audiopath: Audio Path to be registered
 * @return: 0 on success or error code otherwise
 */
int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath)
{
	struct snd_avirt_audiopath_obj *audiopath_obj;
	struct snd_avirt_stream_array stream_array;

	if (!audiopath) {
		D_ERRORK("Audio Path is NULL!");
		return -EINVAL;
	}

	audiopath_obj = snd_avirt_audiopath_create_obj(audiopath->uid);
	if (!audiopath_obj) {
		D_INFOK("Failed to alloc driver object");
		return -ENOMEM;
	}
	audiopath_obj->path = audiopath;

	audiopath->context = audiopath_obj;
	D_INFOK("Registered new Audio Path: %s", audiopath->name);

	list_add_tail(&audiopath_obj->list, &audiopath_list);

	// If we have already configured the streams, configure this AP
	if (core.streams_configured) {
		stream_array.count = 0;
		if (snd_avirt_streams_get(audiopath->uid, &stream_array) > 0)
			audiopath->configure(core.card, &stream_array);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_register);

/**
 * snd_avirt_audiopath_deregister - deregister Audio Path with AVIRT
 * @audiopath: Audio Path to be deregistered
 * @return: 0 on success or error code otherwise
 */
int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath)
{
	struct snd_avirt_audiopath_obj *audiopath_obj;

	// Check if audio path is registered
	if (!audiopath) {
		D_ERRORK("Bad Audio Path Driver");
		return -EINVAL;
	}

	audiopath_obj = audiopath->context;
	if (!audiopath_obj) {
		D_INFOK("driver not registered");
		return -EINVAL;
	}

	list_del(&audiopath_obj->list);
	snd_avirt_audiopath_destroy_obj(audiopath_obj);
	D_INFOK("Deregistered Audio Path %s", audiopath->uid);

	return 0;
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister);

/**
 * snd_avirt_route_create - Create audio route
 * @uid: The unique ID designated to the audio route.
 * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or
 *             SNDRV_PCM_STREAM_CAPTURE)
 * @return: The newly created audio route if successful, or an error pointer
 */
struct snd_avirt_route *snd_avirt_route_create(const char *uid, int direction)
{
	struct snd_avirt_route *route;

	route = kzalloc(sizeof(*route), GFP_KERNEL);
	if (!route)
		return ERR_PTR(-ENOMEM);

	strncpy(route->uid, uid, MAX_NAME_LEN);
	route->channels = 0;
	route->direction = direction;

	return route;
}

/**
 * snd_avirt_stream_create - Create audio stream, including it's ALSA PCM device
 * @name: The name designated to the audio stream
 * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or
 *             SNDRV_PCM_STREAM_CAPTURE)
 * @internal: Whether the PCM should be internal or not
 * @return: The newly created audio stream if successful, or an error pointer
 */
struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
						 int direction, bool internal)
{
	struct snd_avirt_stream *stream;

	if ((core.stream_count + 1) > MAX_STREAMS) {
		D_ERRORK("Cannot add stream %s, PCMs are maxxed out!", name);
		return ERR_PTR(-EPERM);
	}

	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
	if (!stream)
		return ERR_PTR(-ENOMEM);

	strncpy(stream->name, name, MAX_NAME_LEN);
	strncpy(stream->map, "none", MAX_NAME_LEN);
	stream->channels = 0;
	stream->direction = direction;
	stream->internal = internal;
	stream->device = core.stream_count++;

	/* Initialize PCM ops table for this stream.
	 * Will be populated once map is known */
	stream->pcm_ops = kzalloc(sizeof(struct snd_pcm_ops), GFP_KERNEL);
	if (!stream->pcm_ops) {
		D_ERRORK("Failed to allocate PCM ops table");
		return ERR_PTR(-EFAULT);
	}
	memcpy(stream->pcm_ops, &pcm_ops_avirt, sizeof(struct snd_pcm_ops));

	if (stream->internal) {
		D_INFOK("name:%s device:%d internal:%d", name, stream->device,
			stream->internal);
	} else {
		D_INFOK("name:%s device:%d", name, stream->device);
	}

	return stream;
}

int snd_avirt_streams_configure(void)
{
	int err = 0, i = 0;
	struct snd_avirt_audiopath_obj *ap_obj;
	struct snd_avirt_stream_array stream_array;

	if (core.streams_configured) {
		D_ERRORK("streams are already configured!");
		return -1;
	}

	list_for_each_entry (ap_obj, &audiopath_list, list) {
		for (i = 0; i < MAX_STREAMS; i++)
			stream_array.streams[i] = NULL;
		stream_array.count = 0;
		if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) <=
		    0)
			continue;

		if (!ap_obj->path->configure) {
			D_ERRORK("Cannot do 'configure' for AP: %s",
				 ap_obj->path->uid);
			return -EFAULT;
		}

		D_INFOK("Do 'configure' for AP: %s streams:%d",
			ap_obj->path->uid, stream_array.count);
		ap_obj->path->configure(core.card, &stream_array);
	}

	core.streams_configured = true;

	return err;
}

int snd_avirt_streams_unconfigure(void)
{
	struct snd_avirt_audiopath_obj *ap_obj;

	if (!core.streams_configured) {
		D_ERRORK("streams are already unconfigured!");
		return -1;
	}

	list_for_each_entry (ap_obj, &audiopath_list, list) {
		if (!ap_obj->path->unconfigure) {
			D_ERRORK("Cannot do 'unconfigure' for AP: %s",
				 ap_obj->path->uid);
			return -EFAULT;
		}

		D_INFOK("Do 'unconfigure' for AP: %s", ap_obj->path->uid);
		ap_obj->path->unconfigure();
	}

	core.streams_configured = false;

	return 0;
}

bool snd_avirt_streams_configured(void)
{
	return core.streams_configured;
}

struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device)
{
	struct snd_avirt_stream *stream;
	struct config_item *item;
	struct list_head *entry;

	if (device >= core.stream_count) {
		D_ERRORK("Stream device number is larger than stream count");
		return ERR_PTR(-EINVAL);
	}

	list_for_each (entry, &core.stream_group->cg_children) {
		item = container_of(entry, struct config_item, ci_entry);
		stream = snd_avirt_stream_from_config_item(item);
		if (!stream)
			return ERR_PTR(-EFAULT);
		if (stream->device == device)
			return stream;
	}

	return NULL;
}

static int snd_avirt_core_probe(struct platform_device *devptr)
{
	int err;

	// Create the card instance
	err = snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt",
			   THIS_MODULE, 0, &core.card);
	if (err < 0) {
		D_ERRORK("Failed to create sound card");
		return err;
	}

	strncpy(core.card->driver, "avirt-alsa-dev", 16);
	strncpy(core.card->shortname, "avirt", 32);
	strncpy(core.card->longname, "A virtual sound card driver for ALSA",
		80);

	return 0;
}

static int snd_avirt_core_remove(struct platform_device *devptr)
{
	snd_card_free(core.card);

	return 0;
}

static struct platform_driver snd_avirt_driver = {
	.probe = snd_avirt_core_probe,
	.remove = snd_avirt_core_remove,
	.driver =
		{
			.name = SND_AVIRT_DRIVER,
		},
};

/**
 * snd_avirt_core_init - Initialize the kernel module
 */
static int __init snd_avirt_core_init(void)
{
	int err;

	D_INFOK("Alsa Virtual Sound Driver avirt-%d.%d.%d", core.version[0],
		core.version[1], core.version[2]);

	err = platform_driver_register(&snd_avirt_driver);
	if (err < 0)
		return err;

	core.plat_dev =
		platform_device_register_simple(SND_AVIRT_DRIVER, 0, NULL, 0);
	if (IS_ERR(core.plat_dev)) {
		err = PTR_ERR(core.plat_dev);
		goto exit_platform_device;
	}

	core.class = class_create(THIS_MODULE, SND_AVIRT_DRIVER);
	if (IS_ERR(core.class)) {
		D_ERRORK("No udev support");
		return PTR_ERR(core.class);
	}

	core.dev = device_create(core.class, NULL, 0, NULL, "core");
	if (IS_ERR(core.dev)) {
		err = PTR_ERR(core.dev);
		goto exit_class;
	}

	err = snd_avirt_sysfs_init(&core);
	if (err < 0)
		goto exit_class_container;

	err = snd_card_register(core.card);
	if (err < 0) {
		D_ERRORK("Sound card registration failed!");
		snd_card_free(core.card);
	}

	err = snd_avirt_configfs_init(&core);
	if (err < 0)
		goto exit_sysfs;

	return 0;

exit_sysfs:
	snd_avirt_sysfs_exit(&core);
exit_class_container:
	device_destroy(core.class, 0);
exit_class:
	class_destroy(core.class);
exit_platform_device:
	platform_device_unregister(core.plat_dev);
	platform_driver_unregister(&snd_avirt_driver);

	return err;
}

/**
 * snd_avirt_core_exit - Destroy the kernel module
 */
static void __exit snd_avirt_core_exit(void)
{
	snd_avirt_configfs_exit(&core);
	snd_avirt_sysfs_exit(&core);
	device_destroy(core.class, 0);
	class_destroy(core.class);
	platform_device_unregister(core.plat_dev);
	platform_driver_unregister(&snd_avirt_driver);
}

module_init(snd_avirt_core_init);
module_exit(snd_avirt_core_exit);