Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: xen-front: Read sound driver configuration from Xen store

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

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Oleksandr Andrushchenko and committed by
Takashi Iwai
fd3b3604 cc3196ae

+576 -1
+2 -1
sound/xen/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 OR MIT 2 2 3 - snd_xen_front-objs := xen_snd_front.o 3 + snd_xen_front-objs := xen_snd_front.o \ 4 + xen_snd_front_cfg.o 4 5 5 6 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
+7
sound/xen/xen_snd_front.c
··· 25 25 26 26 static int sndback_initwait(struct xen_snd_front_info *front_info) 27 27 { 28 + int num_streams; 29 + int ret; 30 + 31 + ret = xen_snd_front_cfg_card(front_info, &num_streams); 32 + if (ret < 0) 33 + return ret; 34 + 28 35 return 0; 29 36 } 30 37
+4
sound/xen/xen_snd_front.h
··· 11 11 #ifndef __XEN_SND_FRONT_H 12 12 #define __XEN_SND_FRONT_H 13 13 14 + #include "xen_snd_front_cfg.h" 15 + 14 16 struct xen_snd_front_info { 15 17 struct xenbus_device *xb_dev; 18 + 19 + struct xen_front_cfg_card cfg; 16 20 }; 17 21 18 22 #endif /* __XEN_SND_FRONT_H */
+517
sound/xen/xen_snd_front_cfg.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 OR MIT 2 + 3 + /* 4 + * Xen para-virtual sound device 5 + * 6 + * Copyright (C) 2016-2018 EPAM Systems Inc. 7 + * 8 + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 9 + */ 10 + 11 + #include <xen/xenbus.h> 12 + 13 + #include <xen/interface/io/sndif.h> 14 + 15 + #include "xen_snd_front.h" 16 + #include "xen_snd_front_cfg.h" 17 + 18 + /* Maximum number of supported streams. */ 19 + #define VSND_MAX_STREAM 8 20 + 21 + struct cfg_hw_sample_rate { 22 + const char *name; 23 + unsigned int mask; 24 + unsigned int value; 25 + }; 26 + 27 + static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = { 28 + { .name = "5512", .mask = SNDRV_PCM_RATE_5512, .value = 5512 }, 29 + { .name = "8000", .mask = SNDRV_PCM_RATE_8000, .value = 8000 }, 30 + { .name = "11025", .mask = SNDRV_PCM_RATE_11025, .value = 11025 }, 31 + { .name = "16000", .mask = SNDRV_PCM_RATE_16000, .value = 16000 }, 32 + { .name = "22050", .mask = SNDRV_PCM_RATE_22050, .value = 22050 }, 33 + { .name = "32000", .mask = SNDRV_PCM_RATE_32000, .value = 32000 }, 34 + { .name = "44100", .mask = SNDRV_PCM_RATE_44100, .value = 44100 }, 35 + { .name = "48000", .mask = SNDRV_PCM_RATE_48000, .value = 48000 }, 36 + { .name = "64000", .mask = SNDRV_PCM_RATE_64000, .value = 64000 }, 37 + { .name = "96000", .mask = SNDRV_PCM_RATE_96000, .value = 96000 }, 38 + { .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 }, 39 + { .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 }, 40 + }; 41 + 42 + struct cfg_hw_sample_format { 43 + const char *name; 44 + u64 mask; 45 + }; 46 + 47 + static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = { 48 + { 49 + .name = XENSND_PCM_FORMAT_U8_STR, 50 + .mask = SNDRV_PCM_FMTBIT_U8 51 + }, 52 + { 53 + .name = XENSND_PCM_FORMAT_S8_STR, 54 + .mask = SNDRV_PCM_FMTBIT_S8 55 + }, 56 + { 57 + .name = XENSND_PCM_FORMAT_U16_LE_STR, 58 + .mask = SNDRV_PCM_FMTBIT_U16_LE 59 + }, 60 + { 61 + .name = XENSND_PCM_FORMAT_U16_BE_STR, 62 + .mask = SNDRV_PCM_FMTBIT_U16_BE 63 + }, 64 + { 65 + .name = XENSND_PCM_FORMAT_S16_LE_STR, 66 + .mask = SNDRV_PCM_FMTBIT_S16_LE 67 + }, 68 + { 69 + .name = XENSND_PCM_FORMAT_S16_BE_STR, 70 + .mask = SNDRV_PCM_FMTBIT_S16_BE 71 + }, 72 + { 73 + .name = XENSND_PCM_FORMAT_U24_LE_STR, 74 + .mask = SNDRV_PCM_FMTBIT_U24_LE 75 + }, 76 + { 77 + .name = XENSND_PCM_FORMAT_U24_BE_STR, 78 + .mask = SNDRV_PCM_FMTBIT_U24_BE 79 + }, 80 + { 81 + .name = XENSND_PCM_FORMAT_S24_LE_STR, 82 + .mask = SNDRV_PCM_FMTBIT_S24_LE 83 + }, 84 + { 85 + .name = XENSND_PCM_FORMAT_S24_BE_STR, 86 + .mask = SNDRV_PCM_FMTBIT_S24_BE 87 + }, 88 + { 89 + .name = XENSND_PCM_FORMAT_U32_LE_STR, 90 + .mask = SNDRV_PCM_FMTBIT_U32_LE 91 + }, 92 + { 93 + .name = XENSND_PCM_FORMAT_U32_BE_STR, 94 + .mask = SNDRV_PCM_FMTBIT_U32_BE 95 + }, 96 + { 97 + .name = XENSND_PCM_FORMAT_S32_LE_STR, 98 + .mask = SNDRV_PCM_FMTBIT_S32_LE 99 + }, 100 + { 101 + .name = XENSND_PCM_FORMAT_S32_BE_STR, 102 + .mask = SNDRV_PCM_FMTBIT_S32_BE 103 + }, 104 + { 105 + .name = XENSND_PCM_FORMAT_A_LAW_STR, 106 + .mask = SNDRV_PCM_FMTBIT_A_LAW 107 + }, 108 + { 109 + .name = XENSND_PCM_FORMAT_MU_LAW_STR, 110 + .mask = SNDRV_PCM_FMTBIT_MU_LAW 111 + }, 112 + { 113 + .name = XENSND_PCM_FORMAT_F32_LE_STR, 114 + .mask = SNDRV_PCM_FMTBIT_FLOAT_LE 115 + }, 116 + { 117 + .name = XENSND_PCM_FORMAT_F32_BE_STR, 118 + .mask = SNDRV_PCM_FMTBIT_FLOAT_BE 119 + }, 120 + { 121 + .name = XENSND_PCM_FORMAT_F64_LE_STR, 122 + .mask = SNDRV_PCM_FMTBIT_FLOAT64_LE 123 + }, 124 + { 125 + .name = XENSND_PCM_FORMAT_F64_BE_STR, 126 + .mask = SNDRV_PCM_FMTBIT_FLOAT64_BE 127 + }, 128 + { 129 + .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR, 130 + .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE 131 + }, 132 + { 133 + .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR, 134 + .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE 135 + }, 136 + { 137 + .name = XENSND_PCM_FORMAT_IMA_ADPCM_STR, 138 + .mask = SNDRV_PCM_FMTBIT_IMA_ADPCM 139 + }, 140 + { 141 + .name = XENSND_PCM_FORMAT_MPEG_STR, 142 + .mask = SNDRV_PCM_FMTBIT_MPEG 143 + }, 144 + { 145 + .name = XENSND_PCM_FORMAT_GSM_STR, 146 + .mask = SNDRV_PCM_FMTBIT_GSM 147 + }, 148 + }; 149 + 150 + static void cfg_hw_rates(char *list, unsigned int len, 151 + const char *path, struct snd_pcm_hardware *pcm_hw) 152 + { 153 + char *cur_rate; 154 + unsigned int cur_mask; 155 + unsigned int cur_value; 156 + unsigned int rates; 157 + unsigned int rate_min; 158 + unsigned int rate_max; 159 + int i; 160 + 161 + rates = 0; 162 + rate_min = -1; 163 + rate_max = 0; 164 + while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) { 165 + for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++) 166 + if (!strncasecmp(cur_rate, 167 + CFG_HW_SUPPORTED_RATES[i].name, 168 + XENSND_SAMPLE_RATE_MAX_LEN)) { 169 + cur_mask = CFG_HW_SUPPORTED_RATES[i].mask; 170 + cur_value = CFG_HW_SUPPORTED_RATES[i].value; 171 + rates |= cur_mask; 172 + if (rate_min > cur_value) 173 + rate_min = cur_value; 174 + if (rate_max < cur_value) 175 + rate_max = cur_value; 176 + } 177 + } 178 + 179 + if (rates) { 180 + pcm_hw->rates = rates; 181 + pcm_hw->rate_min = rate_min; 182 + pcm_hw->rate_max = rate_max; 183 + } 184 + } 185 + 186 + static void cfg_formats(char *list, unsigned int len, 187 + const char *path, struct snd_pcm_hardware *pcm_hw) 188 + { 189 + u64 formats; 190 + char *cur_format; 191 + int i; 192 + 193 + formats = 0; 194 + while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) { 195 + for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++) 196 + if (!strncasecmp(cur_format, 197 + CFG_HW_SUPPORTED_FORMATS[i].name, 198 + XENSND_SAMPLE_FORMAT_MAX_LEN)) 199 + formats |= CFG_HW_SUPPORTED_FORMATS[i].mask; 200 + } 201 + 202 + if (formats) 203 + pcm_hw->formats = formats; 204 + } 205 + 206 + #define MAX_BUFFER_SIZE (64 * 1024) 207 + #define MIN_PERIOD_SIZE 64 208 + #define MAX_PERIOD_SIZE MAX_BUFFER_SIZE 209 + #define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | \ 210 + SNDRV_PCM_FMTBIT_S16_LE) 211 + #define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | \ 212 + SNDRV_PCM_RATE_8000_48000) 213 + #define USE_RATE_MIN 5512 214 + #define USE_RATE_MAX 48000 215 + #define USE_CHANNELS_MIN 1 216 + #define USE_CHANNELS_MAX 2 217 + #define USE_PERIODS_MIN 2 218 + #define USE_PERIODS_MAX (MAX_BUFFER_SIZE / MIN_PERIOD_SIZE) 219 + 220 + static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = { 221 + .info = (SNDRV_PCM_INFO_MMAP | 222 + SNDRV_PCM_INFO_INTERLEAVED | 223 + SNDRV_PCM_INFO_RESUME | 224 + SNDRV_PCM_INFO_MMAP_VALID), 225 + .formats = USE_FORMATS, 226 + .rates = USE_RATE, 227 + .rate_min = USE_RATE_MIN, 228 + .rate_max = USE_RATE_MAX, 229 + .channels_min = USE_CHANNELS_MIN, 230 + .channels_max = USE_CHANNELS_MAX, 231 + .buffer_bytes_max = MAX_BUFFER_SIZE, 232 + .period_bytes_min = MIN_PERIOD_SIZE, 233 + .period_bytes_max = MAX_PERIOD_SIZE, 234 + .periods_min = USE_PERIODS_MIN, 235 + .periods_max = USE_PERIODS_MAX, 236 + .fifo_size = 0, 237 + }; 238 + 239 + static void cfg_read_pcm_hw(const char *path, 240 + struct snd_pcm_hardware *parent_pcm_hw, 241 + struct snd_pcm_hardware *pcm_hw) 242 + { 243 + char *list; 244 + int val; 245 + size_t buf_sz; 246 + unsigned int len; 247 + 248 + /* Inherit parent's PCM HW and read overrides from XenStore. */ 249 + if (parent_pcm_hw) 250 + *pcm_hw = *parent_pcm_hw; 251 + else 252 + *pcm_hw = SND_DRV_PCM_HW_DEFAULT; 253 + 254 + val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0); 255 + if (val) 256 + pcm_hw->channels_min = val; 257 + 258 + val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0); 259 + if (val) 260 + pcm_hw->channels_max = val; 261 + 262 + list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len); 263 + if (!IS_ERR(list)) { 264 + cfg_hw_rates(list, len, path, pcm_hw); 265 + kfree(list); 266 + } 267 + 268 + list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len); 269 + if (!IS_ERR(list)) { 270 + cfg_formats(list, len, path, pcm_hw); 271 + kfree(list); 272 + } 273 + 274 + buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0); 275 + if (buf_sz) 276 + pcm_hw->buffer_bytes_max = buf_sz; 277 + 278 + /* Update configuration to match new values. */ 279 + if (pcm_hw->channels_min > pcm_hw->channels_max) 280 + pcm_hw->channels_min = pcm_hw->channels_max; 281 + 282 + if (pcm_hw->rate_min > pcm_hw->rate_max) 283 + pcm_hw->rate_min = pcm_hw->rate_max; 284 + 285 + pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max; 286 + 287 + pcm_hw->periods_max = pcm_hw->period_bytes_max / 288 + pcm_hw->period_bytes_min; 289 + } 290 + 291 + static int cfg_get_stream_type(const char *path, int index, 292 + int *num_pb, int *num_cap) 293 + { 294 + char *str = NULL; 295 + char *stream_path; 296 + int ret; 297 + 298 + *num_pb = 0; 299 + *num_cap = 0; 300 + stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index); 301 + if (!stream_path) { 302 + ret = -ENOMEM; 303 + goto fail; 304 + } 305 + 306 + str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); 307 + if (IS_ERR(str)) { 308 + ret = PTR_ERR(str); 309 + goto fail; 310 + } 311 + 312 + if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, 313 + sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { 314 + (*num_pb)++; 315 + } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, 316 + sizeof(XENSND_STREAM_TYPE_CAPTURE))) { 317 + (*num_cap)++; 318 + } else { 319 + ret = -EINVAL; 320 + goto fail; 321 + } 322 + ret = 0; 323 + 324 + fail: 325 + kfree(stream_path); 326 + kfree(str); 327 + return ret; 328 + } 329 + 330 + static int cfg_stream(struct xen_snd_front_info *front_info, 331 + struct xen_front_cfg_pcm_instance *pcm_instance, 332 + const char *path, int index, int *cur_pb, int *cur_cap, 333 + int *stream_cnt) 334 + { 335 + char *str = NULL; 336 + char *stream_path; 337 + struct xen_front_cfg_stream *stream; 338 + int ret; 339 + 340 + stream_path = devm_kasprintf(&front_info->xb_dev->dev, 341 + GFP_KERNEL, "%s/%d", path, index); 342 + if (!stream_path) { 343 + ret = -ENOMEM; 344 + goto fail; 345 + } 346 + 347 + str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); 348 + if (IS_ERR(str)) { 349 + ret = PTR_ERR(str); 350 + goto fail; 351 + } 352 + 353 + if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, 354 + sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { 355 + stream = &pcm_instance->streams_pb[(*cur_pb)++]; 356 + } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, 357 + sizeof(XENSND_STREAM_TYPE_CAPTURE))) { 358 + stream = &pcm_instance->streams_cap[(*cur_cap)++]; 359 + } else { 360 + ret = -EINVAL; 361 + goto fail; 362 + } 363 + 364 + /* Get next stream index. */ 365 + stream->index = (*stream_cnt)++; 366 + stream->xenstore_path = stream_path; 367 + /* 368 + * Check XenStore if PCM HW configuration exists for this stream 369 + * and update if so, e.g. we inherit all values from device's PCM HW, 370 + * but can still override some of the values for the stream. 371 + */ 372 + cfg_read_pcm_hw(stream->xenstore_path, 373 + &pcm_instance->pcm_hw, &stream->pcm_hw); 374 + ret = 0; 375 + 376 + fail: 377 + kfree(str); 378 + return ret; 379 + } 380 + 381 + static int cfg_device(struct xen_snd_front_info *front_info, 382 + struct xen_front_cfg_pcm_instance *pcm_instance, 383 + struct snd_pcm_hardware *parent_pcm_hw, 384 + const char *path, int node_index, int *stream_cnt) 385 + { 386 + char *str; 387 + char *device_path; 388 + int ret, i, num_streams; 389 + int num_pb, num_cap; 390 + int cur_pb, cur_cap; 391 + char node[3]; 392 + 393 + device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index); 394 + if (!device_path) 395 + return -ENOMEM; 396 + 397 + str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL); 398 + if (!IS_ERR(str)) { 399 + strncpy(pcm_instance->name, str, sizeof(pcm_instance->name)); 400 + kfree(str); 401 + } 402 + 403 + pcm_instance->device_id = node_index; 404 + 405 + /* 406 + * Check XenStore if PCM HW configuration exists for this device 407 + * and update if so, e.g. we inherit all values from card's PCM HW, 408 + * but can still override some of the values for the device. 409 + */ 410 + cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw); 411 + 412 + /* Find out how many streams were configured in Xen store. */ 413 + num_streams = 0; 414 + do { 415 + snprintf(node, sizeof(node), "%d", num_streams); 416 + if (!xenbus_exists(XBT_NIL, device_path, node)) 417 + break; 418 + 419 + num_streams++; 420 + } while (num_streams < VSND_MAX_STREAM); 421 + 422 + pcm_instance->num_streams_pb = 0; 423 + pcm_instance->num_streams_cap = 0; 424 + /* Get number of playback and capture streams. */ 425 + for (i = 0; i < num_streams; i++) { 426 + ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap); 427 + if (ret < 0) 428 + goto fail; 429 + 430 + pcm_instance->num_streams_pb += num_pb; 431 + pcm_instance->num_streams_cap += num_cap; 432 + } 433 + 434 + if (pcm_instance->num_streams_pb) { 435 + pcm_instance->streams_pb = 436 + devm_kcalloc(&front_info->xb_dev->dev, 437 + pcm_instance->num_streams_pb, 438 + sizeof(struct xen_front_cfg_stream), 439 + GFP_KERNEL); 440 + if (!pcm_instance->streams_pb) { 441 + ret = -ENOMEM; 442 + goto fail; 443 + } 444 + } 445 + 446 + if (pcm_instance->num_streams_cap) { 447 + pcm_instance->streams_cap = 448 + devm_kcalloc(&front_info->xb_dev->dev, 449 + pcm_instance->num_streams_cap, 450 + sizeof(struct xen_front_cfg_stream), 451 + GFP_KERNEL); 452 + if (!pcm_instance->streams_cap) { 453 + ret = -ENOMEM; 454 + goto fail; 455 + } 456 + } 457 + 458 + cur_pb = 0; 459 + cur_cap = 0; 460 + for (i = 0; i < num_streams; i++) { 461 + ret = cfg_stream(front_info, pcm_instance, device_path, i, 462 + &cur_pb, &cur_cap, stream_cnt); 463 + if (ret < 0) 464 + goto fail; 465 + } 466 + ret = 0; 467 + 468 + fail: 469 + kfree(device_path); 470 + return ret; 471 + } 472 + 473 + int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info, 474 + int *stream_cnt) 475 + { 476 + struct xenbus_device *xb_dev = front_info->xb_dev; 477 + struct xen_front_cfg_card *cfg = &front_info->cfg; 478 + int ret, num_devices, i; 479 + char node[3]; 480 + 481 + *stream_cnt = 0; 482 + num_devices = 0; 483 + do { 484 + snprintf(node, sizeof(node), "%d", num_devices); 485 + if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node)) 486 + break; 487 + 488 + num_devices++; 489 + } while (num_devices < SNDRV_PCM_DEVICES); 490 + 491 + if (!num_devices) { 492 + dev_warn(&xb_dev->dev, 493 + "No devices configured for sound card at %s\n", 494 + xb_dev->nodename); 495 + return -ENODEV; 496 + } 497 + 498 + /* Start from default PCM HW configuration for the card. */ 499 + cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw); 500 + 501 + cfg->pcm_instances = 502 + devm_kcalloc(&front_info->xb_dev->dev, num_devices, 503 + sizeof(struct xen_front_cfg_pcm_instance), 504 + GFP_KERNEL); 505 + if (!cfg->pcm_instances) 506 + return -ENOMEM; 507 + 508 + for (i = 0; i < num_devices; i++) { 509 + ret = cfg_device(front_info, &cfg->pcm_instances[i], 510 + &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt); 511 + if (ret < 0) 512 + return ret; 513 + } 514 + cfg->num_pcm_instances = num_devices; 515 + return 0; 516 + } 517 +
+46
sound/xen/xen_snd_front_cfg.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 + 3 + /* 4 + * Xen para-virtual sound device 5 + * 6 + * Copyright (C) 2016-2018 EPAM Systems Inc. 7 + * 8 + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 9 + */ 10 + 11 + #ifndef __XEN_SND_FRONT_CFG_H 12 + #define __XEN_SND_FRONT_CFG_H 13 + 14 + #include <sound/core.h> 15 + #include <sound/pcm.h> 16 + 17 + struct xen_snd_front_info; 18 + 19 + struct xen_front_cfg_stream { 20 + int index; 21 + char *xenstore_path; 22 + struct snd_pcm_hardware pcm_hw; 23 + }; 24 + 25 + struct xen_front_cfg_pcm_instance { 26 + char name[80]; 27 + int device_id; 28 + struct snd_pcm_hardware pcm_hw; 29 + int num_streams_pb; 30 + struct xen_front_cfg_stream *streams_pb; 31 + int num_streams_cap; 32 + struct xen_front_cfg_stream *streams_cap; 33 + }; 34 + 35 + struct xen_front_cfg_card { 36 + char name_short[32]; 37 + char name_long[80]; 38 + struct snd_pcm_hardware pcm_hw; 39 + int num_pcm_instances; 40 + struct xen_front_cfg_pcm_instance *pcm_instances; 41 + }; 42 + 43 + int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info, 44 + int *stream_cnt); 45 + 46 + #endif /* __XEN_SND_FRONT_CFG_H */