From patchwork Mon Aug 7 07:43:08 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksandr Andrushchenko X-Patchwork-Id: 9884635 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 330F9602CC for ; Mon, 7 Aug 2017 07:46:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 218E1285B7 for ; Mon, 7 Aug 2017 07:46:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 163AE285C4; Mon, 7 Aug 2017 07:46:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-3.6 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 2786D285DE for ; Mon, 7 Aug 2017 07:46:50 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dechR-0005ND-MW; Mon, 07 Aug 2017 07:43:41 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dechQ-0005N1-97 for xen-devel@lists.xen.org; Mon, 07 Aug 2017 07:43:40 +0000 Received: from [193.109.254.147] by server-5.bemta-6.messagelabs.com id 96/C3-03368-B2A18895; Mon, 07 Aug 2017 07:43:39 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrPIsWRWlGSWpSXmKPExsVyMfS6s66WVEe kwdyNKhZLPi5mcWD0OLr7N1MAYxRrZl5SfkUCa8asBT9ZChqLK9avamRsYDyb0MXIxSEkMIFR onXmFZYuRk4OFoGXLBJz7laCJCQE+lklZky5C5aQEIiTWDV/GROEXSax5vg5MFtIQFHi67PpT BCTVjJJnHi5iQ0kwSZgJLH8xg+wZhGBCIljjz+AxZkFSiVu7J/D3MXIwSEsECjRvV8dYrGqxN JvbUwgYV4BJ4kPb3ggVslJ3DzXyQxicwo4S5w/v4YVpEQIqOTEq9QJjAILGBlWMWoUpxaVpRb pGhroJRVlpmeU5CZm5gB5Znq5qcXFiempOYlJxXrJ+bmbGIFBxQAEOxiPv487xCjJwaQkyhu/ oi1SiC8pP6UyI7E4I76oNCe1+BCjDAeHkgTvPYmOSCHBotT01Iq0zBxgeMOkJTh4lER4V4Oke YsLEnOLM9MhUqcY7TmuXFn3hYljw+r1QHLKge1A8tWE/9+YhFjy8vNSpcR5tSWB2gRA2jJK8+ CGwuLxEqOslDAvI9CZQjwFqUW5mSWo8q8YxTkYlYR55UCm8GTmlcDtfgV0FhPQWW8SW0HOKkl ESEk1MC6u+rBt6Z3k5sY/KepGBWrHIn2iAqLMbgSp7z8Y+efxkZ8P/L7rBjKcSLooWGPHd4L7 w11Zgf3T372zvrDitbKxpVGsqv91A5YJ1/wrPX+pXgoNcyy+YvNl6vbkitSl/7ozq5OcFx5ee z/ucIL6r+Tp799d63mT3TbJ0N+59syTc1t/Obq1ZCixFGckGmoxFxUnAgBt5+KzwgIAAA== X-Env-Sender: andr2000@gmail.com X-Msg-Ref: server-8.tower-27.messagelabs.com!1502091818!100344671!1 X-Originating-IP: [209.85.215.67] X-SpamReason: No, hits=0.0 required=7.0 tests=UPPERCASE_25_50 X-StarScan-Received: X-StarScan-Version: 9.4.45; banners=-,-,- X-VirusChecked: Checked Received: (qmail 21946 invoked from network); 7 Aug 2017 07:43:38 -0000 Received: from mail-lf0-f67.google.com (HELO mail-lf0-f67.google.com) (209.85.215.67) by server-8.tower-27.messagelabs.com with AES128-GCM-SHA256 encrypted SMTP; 7 Aug 2017 07:43:38 -0000 Received: by mail-lf0-f67.google.com with SMTP id x16so4868147lfb.4 for ; Mon, 07 Aug 2017 00:43:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=M3L5C2lRmQxkArolKXANeicDntK9IqGCNeWtMCEj3Kw=; b=VdBXrC3H0o0xrUg439dXXkS7yJEWtN5rBAw0unoUeOpGURTqi8kna4L+a4M72qZ7Sa GnvVK2zFfWr4mp0ltFa9p4l7ikpkQ2sDFVHPf4+sjlPFZucriN4Ybb7SnIzWA3cHmFpC Z4mJMXGHSoJRTQtu73+VLQivPMPjor1eaHhY5CJbcLtrJ+5DTBA6BJm3JgrEmcYuGhJe lShD0ZuWm69QG0oVE6uEIND9dC7tXvti0bMp0mtId+P1J3+eBX2dUM7iMARRnCR6zlfn lawY0wKzGbaJ3bFhDimsDAYRtEI8NY6CmPGKCMjPpyh9lZN7iyX8Mg3936BNeEwtH1F2 DCZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=M3L5C2lRmQxkArolKXANeicDntK9IqGCNeWtMCEj3Kw=; b=b+1e5mjiFw4RW2xqeGCN4zhj/tau9pAsA41dugMmfdwi2U8yNv55dV85FD6uCzV963 qRnO+jk6+ANvX3wgUFZji16yy7+NUJ0EqmsBh+eGtHgP/U/wf4At9QnTRCIFi29ca06M J6XiJJldiPBeNizZ+mXgnuDJkNjrfGBB1yEBuEY9+6PIc+VhrqQfpuCZq1Obm0CfUgDI c6uws4xVL6YclOzVDbEiMAfEeojVQMUk3Qh9LLQZFrgrRU6pb7mr9mVjXKVrfkVtnrgX TSRnFWu+x43YqK4V7k5csgxDLSAXM0Pvh2NxcKiorWx/6WD4xzDZFYc97zHDDBduOmGW EnWw== X-Gm-Message-State: AHYfb5iX3rwkoY3Q/FnwdduQOeAAEoUeKBSb1YIlgvAa3/mFbxnRPKIe nMv3MBePHVTfcQ== X-Received: by 10.46.2.197 with SMTP id y66mr482376lje.54.1502091817690; Mon, 07 Aug 2017 00:43:37 -0700 (PDT) Received: from a2k-HP-ProDesk-600-G2-SFF.kyiv.epam.com (ll-52.209.223.85.sovam.net.ua. [85.223.209.52]) by smtp.gmail.com with ESMTPSA id f136sm399062lff.34.2017.08.07.00.43.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 07 Aug 2017 00:43:37 -0700 (PDT) From: Oleksandr Andrushchenko To: alsa-devel@alsa-project.org, xen-devel@lists.xen.org, linux-kernel@vger.kernel.org Date: Mon, 7 Aug 2017 10:43:08 +0300 Message-Id: <1502091796-14413-4-git-send-email-andr2000@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1502091796-14413-1-git-send-email-andr2000@gmail.com> References: <1502091796-14413-1-git-send-email-andr2000@gmail.com> Cc: andr2000@gmail.com, Oleksandr Andrushchenko , tiwai@suse.com, perex@perex.cz Subject: [Xen-devel] [PATCH 03/11] ALSA: vsnd: Read sound driver configuration from Xen store X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Oleksandr Andrushchenko Read configuration values from Xen store according to xen/interface/io/sndif.h protocol: - introduce configuration structures for different components, e.g. sound card, device, stream - read PCM HW parameters, e.g rate, format etc. - detect stream type (capture/playback) - read device and card parameters Fill in platform data with the configuration read, so it can be passed to sound driver later. Signed-off-by: Oleksandr Andrushchenko --- sound/drivers/xen-front.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c index c4fd21cac3a7..ef48cbf44cf2 100644 --- a/sound/drivers/xen-front.c +++ b/sound/drivers/xen-front.c @@ -20,24 +20,554 @@ #include +#include +#include + #include #include #include #include +/* maximum number of supported streams */ +#define VSND_MAX_STREAM 8 + +struct cfg_stream { + int unique_id; + char *xenstore_path; + struct snd_pcm_hardware pcm_hw; +}; + +struct cfg_pcm_instance { + char name[80]; + int device_id; + struct snd_pcm_hardware pcm_hw; + int num_streams_pb; + struct cfg_stream *streams_pb; + int num_streams_cap; + struct cfg_stream *streams_cap; +}; + +struct cfg_card { + char name_short[32]; + char name_long[80]; + struct snd_pcm_hardware pcm_hw; + int num_pcm_instances; + struct cfg_pcm_instance *pcm_instances; +}; + +struct sdev_card_plat_data { + struct xdrv_info *xdrv_info; + struct cfg_card cfg_card; +}; + struct xdrv_info { struct xenbus_device *xb_dev; spinlock_t io_lock; struct mutex mutex; + struct sdev_card_plat_data cfg_plat_data; }; +#define MAX_XEN_BUFFER_SIZE (64 * 1024) +#define MAX_BUFFER_SIZE MAX_XEN_BUFFER_SIZE +#define MIN_PERIOD_SIZE 64 +#define MAX_PERIOD_SIZE (MAX_BUFFER_SIZE / 8) +#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE) +#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | \ + SNDRV_PCM_RATE_8000_48000) +#define USE_RATE_MIN 5512 +#define USE_RATE_MAX 48000 +#define USE_CHANNELS_MIN 1 +#define USE_CHANNELS_MAX 2 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 8 + +static struct snd_pcm_hardware sdrv_pcm_hw_default = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = USE_RATE, + .rate_min = USE_RATE_MIN, + .rate_max = USE_RATE_MAX, + .channels_min = USE_CHANNELS_MIN, + .channels_max = USE_CHANNELS_MAX, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = MIN_PERIOD_SIZE, + .period_bytes_max = MAX_PERIOD_SIZE, + .periods_min = USE_PERIODS_MIN, + .periods_max = USE_PERIODS_MAX, + .fifo_size = 0, +}; + +struct CFG_HW_SAMPLE_RATE { + const char *name; + unsigned int mask; + unsigned int value; +}; + +static struct CFG_HW_SAMPLE_RATE cfg_hw_supported_rates[] = { + { .name = "5512", .mask = SNDRV_PCM_RATE_5512, .value = 5512 }, + { .name = "8000", .mask = SNDRV_PCM_RATE_8000, .value = 8000 }, + { .name = "11025", .mask = SNDRV_PCM_RATE_11025, .value = 11025 }, + { .name = "16000", .mask = SNDRV_PCM_RATE_16000, .value = 16000 }, + { .name = "22050", .mask = SNDRV_PCM_RATE_22050, .value = 22050 }, + { .name = "32000", .mask = SNDRV_PCM_RATE_32000, .value = 32000 }, + { .name = "44100", .mask = SNDRV_PCM_RATE_44100, .value = 44100 }, + { .name = "48000", .mask = SNDRV_PCM_RATE_48000, .value = 48000 }, + { .name = "64000", .mask = SNDRV_PCM_RATE_64000, .value = 64000 }, + { .name = "96000", .mask = SNDRV_PCM_RATE_96000, .value = 96000 }, + { .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 }, + { .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 }, +}; + +struct CFG_HW_SAMPLE_FORMAT { + const char *name; + u64 mask; +}; + +static struct CFG_HW_SAMPLE_FORMAT cfg_hw_supported_formats[] = { + { + .name = XENSND_PCM_FORMAT_U8_STR, + .mask = SNDRV_PCM_FMTBIT_U8 + }, + { + .name = XENSND_PCM_FORMAT_S8_STR, + .mask = SNDRV_PCM_FMTBIT_S8 + }, + { + .name = XENSND_PCM_FORMAT_U16_LE_STR, + .mask = SNDRV_PCM_FMTBIT_U16_LE + }, + { + .name = XENSND_PCM_FORMAT_U16_BE_STR, + .mask = SNDRV_PCM_FMTBIT_U16_BE + }, + { + .name = XENSND_PCM_FORMAT_S16_LE_STR, + .mask = SNDRV_PCM_FMTBIT_S16_LE + }, + { + .name = XENSND_PCM_FORMAT_S16_BE_STR, + .mask = SNDRV_PCM_FMTBIT_S16_BE + }, + { + .name = XENSND_PCM_FORMAT_U24_LE_STR, + .mask = SNDRV_PCM_FMTBIT_U24_LE + }, + { + .name = XENSND_PCM_FORMAT_U24_BE_STR, + .mask = SNDRV_PCM_FMTBIT_U24_BE + }, + { + .name = XENSND_PCM_FORMAT_S24_LE_STR, + .mask = SNDRV_PCM_FMTBIT_S24_LE + }, + { + .name = XENSND_PCM_FORMAT_S24_BE_STR, + .mask = SNDRV_PCM_FMTBIT_S24_BE + }, + { + .name = XENSND_PCM_FORMAT_U32_LE_STR, + .mask = SNDRV_PCM_FMTBIT_U32_LE + }, + { + .name = XENSND_PCM_FORMAT_U32_BE_STR, + .mask = SNDRV_PCM_FMTBIT_U32_BE + }, + { + .name = XENSND_PCM_FORMAT_S32_LE_STR, + .mask = SNDRV_PCM_FMTBIT_S32_LE + }, + { + .name = XENSND_PCM_FORMAT_S32_BE_STR, + .mask = SNDRV_PCM_FMTBIT_S32_BE + }, + { + .name = XENSND_PCM_FORMAT_A_LAW_STR, + .mask = SNDRV_PCM_FMTBIT_A_LAW + }, + { + .name = XENSND_PCM_FORMAT_MU_LAW_STR, + .mask = SNDRV_PCM_FMTBIT_MU_LAW + }, + { + .name = XENSND_PCM_FORMAT_F32_LE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT_LE + }, + { + .name = XENSND_PCM_FORMAT_F32_BE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT_BE + }, + { + .name = XENSND_PCM_FORMAT_F64_LE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT64_LE + }, + { + .name = XENSND_PCM_FORMAT_F64_BE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT64_BE + }, + { + .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR, + .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE + }, + { + .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR, + .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE + }, + { + .name = XENSND_PCM_FORMAT_IMA_ADPCM_STR, + .mask = SNDRV_PCM_FMTBIT_IMA_ADPCM + }, + { + .name = XENSND_PCM_FORMAT_MPEG_STR, + .mask = SNDRV_PCM_FMTBIT_MPEG + }, + { + .name = XENSND_PCM_FORMAT_GSM_STR, + .mask = SNDRV_PCM_FMTBIT_GSM + }, +}; + +static void cfg_hw_rates(char *list, unsigned int len, + const char *path, struct snd_pcm_hardware *pcm_hw) +{ + char *cur_rate; + unsigned int cur_mask; + unsigned int cur_value; + unsigned int rates; + unsigned int rate_min; + unsigned int rate_max; + int i; + + rates = 0; + rate_min = -1; + rate_max = 0; + while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) { + for (i = 0; i < ARRAY_SIZE(cfg_hw_supported_rates); i++) + if (!strncasecmp(cur_rate, + cfg_hw_supported_rates[i].name, + XENSND_SAMPLE_RATE_MAX_LEN)) { + cur_mask = cfg_hw_supported_rates[i].mask; + cur_value = cfg_hw_supported_rates[i].value; + rates |= cur_mask; + if (rate_min > cur_value) + rate_min = cur_value; + if (rate_max < cur_value) + rate_max = cur_value; + } + } + + if (rates) { + pcm_hw->rates = rates; + pcm_hw->rate_min = rate_min; + pcm_hw->rate_max = rate_max; + } +} + +static void cfg_formats(char *list, unsigned int len, + const char *path, struct snd_pcm_hardware *pcm_hw) +{ + u64 formats; + char *cur_format; + int i; + + formats = 0; + while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) { + for (i = 0; i < ARRAY_SIZE(cfg_hw_supported_formats); i++) + if (!strncasecmp(cur_format, + cfg_hw_supported_formats[i].name, + XENSND_SAMPLE_FORMAT_MAX_LEN)) + formats |= cfg_hw_supported_formats[i].mask; + } + + if (formats) + pcm_hw->formats = formats; +} + +static void cfg_pcm_hw(const char *path, + struct snd_pcm_hardware *parent_pcm_hw, + struct snd_pcm_hardware *pcm_hw) +{ + char *list; + int val; + size_t buf_sz; + unsigned int len; + + /* inherit parent's PCM HW and read overrides if any */ + *pcm_hw = *parent_pcm_hw; + + val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0); + if (val) + pcm_hw->channels_min = val; + + val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0); + if (val) + pcm_hw->channels_max = val; + + list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len); + if (!IS_ERR(list)) { + cfg_hw_rates(list, len, path, pcm_hw); + kfree(list); + } + + list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len); + if (!IS_ERR(list)) { + cfg_formats(list, len, path, pcm_hw); + kfree(list); + } + + buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0); + if (buf_sz) + pcm_hw->buffer_bytes_max = buf_sz; +} + +static int cfg_get_stream_type(const char *path, int index, + int *num_pb, int *num_cap) +{ + char *str = NULL; + char *stream_path; + int ret; + + *num_pb = 0; + *num_cap = 0; + stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index); + if (!stream_path) { + ret = -ENOMEM; + goto fail; + } + + str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); + if (IS_ERR(str)) { + ret = -EINVAL; + goto fail; + } + + if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, + sizeof(XENSND_STREAM_TYPE_PLAYBACK))) + (*num_pb)++; + else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, + sizeof(XENSND_STREAM_TYPE_CAPTURE))) + (*num_cap)++; + else { + ret = -EINVAL; + goto fail; + } + ret = 0; + +fail: + kfree(stream_path); + kfree(str); + return ret; +} + +static int cfg_stream(struct xdrv_info *drv_info, + struct cfg_pcm_instance *pcm_instance, + const char *path, int index, int *cur_pb, int *cur_cap, + int *stream_idx) +{ + char *str = NULL; + char *stream_path; + struct cfg_stream *stream; + int ret; + + stream_path = devm_kasprintf(&drv_info->xb_dev->dev, + GFP_KERNEL, "%s/%d", path, index); + if (!stream_path) { + ret = -ENOMEM; + goto fail; + } + + str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); + if (IS_ERR(str)) { + ret = -EINVAL; + goto fail; + } + + if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, + sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { + stream = &pcm_instance->streams_pb[(*cur_pb)++]; + } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, + sizeof(XENSND_STREAM_TYPE_CAPTURE))) { + stream = &pcm_instance->streams_cap[(*cur_cap)++]; + } else { + ret = -EINVAL; + goto fail; + } + + /* get next stream index */ + stream->unique_id = (*stream_idx)++; + stream->xenstore_path = stream_path; + /* + * check in Xen store if PCM HW configuration exists for this stream + * and update if so, e.g. we inherit all values from device's PCM HW, + * but can still override some of the values for the stream + */ + cfg_pcm_hw(stream->xenstore_path, + &pcm_instance->pcm_hw, &stream->pcm_hw); + ret = 0; + +fail: + kfree(str); + return ret; +} + +static int cfg_device(struct xdrv_info *drv_info, + struct cfg_pcm_instance *pcm_instance, + struct snd_pcm_hardware *parent_pcm_hw, + const char *path, int node_index, int *stream_idx) +{ + char *str; + char *device_path; + int ret, i, num_streams; + int num_pb, num_cap; + int cur_pb, cur_cap; + char node[3]; + + device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index); + if (!device_path) + return -ENOMEM; + + str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL); + if (!IS_ERR(str)) { + strncpy(pcm_instance->name, str, sizeof(pcm_instance->name)); + kfree(str); + } + + pcm_instance->device_id = node_index; + + /* + * check in Xen store if PCM HW configuration exists for this device + * and update if so, e.g. we inherit all values from card's PCM HW, + * but can still override some of the values for the device + */ + cfg_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw); + + /* + * find out how many streams were configured in Xen store: + * streams must have sequential unique IDs, so stop when one + * does not exist + */ + num_streams = 0; + do { + snprintf(node, sizeof(node), "%d", num_streams); + if (!xenbus_exists(XBT_NIL, device_path, node)) + break; + + num_streams++; + } while (num_streams < VSND_MAX_STREAM); + + pcm_instance->num_streams_pb = 0; + pcm_instance->num_streams_cap = 0; + /* get number of playback and capture streams */ + for (i = 0; i < num_streams; i++) { + ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap); + if (ret < 0) + goto fail; + + pcm_instance->num_streams_pb += num_pb; + pcm_instance->num_streams_cap += num_cap; + } + + if (pcm_instance->num_streams_pb) { + pcm_instance->streams_pb = devm_kcalloc( + &drv_info->xb_dev->dev, + pcm_instance->num_streams_pb, + sizeof(struct cfg_stream), GFP_KERNEL); + if (!pcm_instance->streams_pb) { + ret = -ENOMEM; + goto fail; + } + } + + if (pcm_instance->num_streams_cap) { + pcm_instance->streams_cap = devm_kcalloc( + &drv_info->xb_dev->dev, + pcm_instance->num_streams_cap, + sizeof(struct cfg_stream), GFP_KERNEL); + if (!pcm_instance->streams_cap) { + ret = -ENOMEM; + goto fail; + } + } + + cur_pb = 0; + cur_cap = 0; + for (i = 0; i < num_streams; i++) { + ret = cfg_stream(drv_info, + pcm_instance, device_path, i, &cur_pb, &cur_cap, + stream_idx); + if (ret < 0) + goto fail; + } + ret = 0; + +fail: + kfree(device_path); + return ret; +} + +static int cfg_card(struct xdrv_info *drv_info, + struct sdev_card_plat_data *plat_data, int *stream_idx) +{ + struct xenbus_device *xb_dev = drv_info->xb_dev; + int ret, num_devices, i; + char node[3]; + + num_devices = 0; + do { + snprintf(node, sizeof(node), "%d", num_devices); + if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node)) + break; + + num_devices++; + } while (num_devices < SNDRV_PCM_DEVICES); + + if (!num_devices) { + dev_warn(&xb_dev->dev, + "No devices configured for sound card at %s\n", + xb_dev->nodename); + return -ENODEV; + } + + /* start from default PCM HW configuration for the card */ + cfg_pcm_hw(xb_dev->nodename, &sdrv_pcm_hw_default, + &plat_data->cfg_card.pcm_hw); + + plat_data->cfg_card.pcm_instances = devm_kcalloc( + &drv_info->xb_dev->dev, num_devices, + sizeof(struct cfg_pcm_instance), GFP_KERNEL); + if (!plat_data->cfg_card.pcm_instances) + return -ENOMEM; + + for (i = 0; i < num_devices; i++) { + ret = cfg_device(drv_info, + &plat_data->cfg_card.pcm_instances[i], + &plat_data->cfg_card.pcm_hw, + xb_dev->nodename, i, stream_idx); + if (ret < 0) + return ret; + } + plat_data->cfg_card.num_pcm_instances = num_devices; + return 0; +} + static void xdrv_remove_internal(struct xdrv_info *drv_info) { } static int xdrv_be_on_initwait(struct xdrv_info *drv_info) { + int stream_idx; + int ret; + + drv_info->cfg_plat_data.xdrv_info = drv_info; + stream_idx = 0; + ret = cfg_card(drv_info, &drv_info->cfg_plat_data, &stream_idx); + if (ret < 0) + return ret; return 0; }