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 str = NULL; 310 goto fail; 311 } 312 313 if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, 314 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { 315 (*num_pb)++; 316 } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, 317 sizeof(XENSND_STREAM_TYPE_CAPTURE))) { 318 (*num_cap)++; 319 } else { 320 ret = -EINVAL; 321 goto fail; 322 } 323 ret = 0; 324 325 fail: 326 kfree(stream_path); 327 kfree(str); 328 return ret; 329 } 330 331 static int cfg_stream(struct xen_snd_front_info *front_info, 332 struct xen_front_cfg_pcm_instance *pcm_instance, 333 const char *path, int index, int *cur_pb, int *cur_cap, 334 int *stream_cnt) 335 { 336 char *str = NULL; 337 char *stream_path; 338 struct xen_front_cfg_stream *stream; 339 int ret; 340 341 stream_path = devm_kasprintf(&front_info->xb_dev->dev, 342 GFP_KERNEL, "%s/%d", path, index); 343 if (!stream_path) { 344 ret = -ENOMEM; 345 goto fail; 346 } 347 348 str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); 349 if (IS_ERR(str)) { 350 ret = PTR_ERR(str); 351 str = NULL; 352 goto fail; 353 } 354 355 if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, 356 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { 357 stream = &pcm_instance->streams_pb[(*cur_pb)++]; 358 } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, 359 sizeof(XENSND_STREAM_TYPE_CAPTURE))) { 360 stream = &pcm_instance->streams_cap[(*cur_cap)++]; 361 } else { 362 ret = -EINVAL; 363 goto fail; 364 } 365 366 /* Get next stream index. */ 367 stream->index = (*stream_cnt)++; 368 stream->xenstore_path = stream_path; 369 /* 370 * Check XenStore if PCM HW configuration exists for this stream 371 * and update if so, e.g. we inherit all values from device's PCM HW, 372 * but can still override some of the values for the stream. 373 */ 374 cfg_read_pcm_hw(stream->xenstore_path, 375 &pcm_instance->pcm_hw, &stream->pcm_hw); 376 ret = 0; 377 378 fail: 379 kfree(str); 380 return ret; 381 } 382 383 static int cfg_device(struct xen_snd_front_info *front_info, 384 struct xen_front_cfg_pcm_instance *pcm_instance, 385 struct snd_pcm_hardware *parent_pcm_hw, 386 const char *path, int node_index, int *stream_cnt) 387 { 388 char *str; 389 char *device_path; 390 int ret, i, num_streams; 391 int num_pb, num_cap; 392 int cur_pb, cur_cap; 393 char node[3]; 394 395 device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index); 396 if (!device_path) 397 return -ENOMEM; 398 399 str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL); 400 if (!IS_ERR(str)) { 401 strscpy(pcm_instance->name, str, sizeof(pcm_instance->name)); 402 kfree(str); 403 } 404 405 pcm_instance->device_id = node_index; 406 407 /* 408 * Check XenStore if PCM HW configuration exists for this device 409 * and update if so, e.g. we inherit all values from card's PCM HW, 410 * but can still override some of the values for the device. 411 */ 412 cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw); 413 414 /* Find out how many streams were configured in Xen store. */ 415 num_streams = 0; 416 do { 417 snprintf(node, sizeof(node), "%d", num_streams); 418 if (!xenbus_exists(XBT_NIL, device_path, node)) 419 break; 420 421 num_streams++; 422 } while (num_streams < VSND_MAX_STREAM); 423 424 pcm_instance->num_streams_pb = 0; 425 pcm_instance->num_streams_cap = 0; 426 /* Get number of playback and capture streams. */ 427 for (i = 0; i < num_streams; i++) { 428 ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap); 429 if (ret < 0) 430 goto fail; 431 432 pcm_instance->num_streams_pb += num_pb; 433 pcm_instance->num_streams_cap += num_cap; 434 } 435 436 if (pcm_instance->num_streams_pb) { 437 pcm_instance->streams_pb = 438 devm_kcalloc(&front_info->xb_dev->dev, 439 pcm_instance->num_streams_pb, 440 sizeof(struct xen_front_cfg_stream), 441 GFP_KERNEL); 442 if (!pcm_instance->streams_pb) { 443 ret = -ENOMEM; 444 goto fail; 445 } 446 } 447 448 if (pcm_instance->num_streams_cap) { 449 pcm_instance->streams_cap = 450 devm_kcalloc(&front_info->xb_dev->dev, 451 pcm_instance->num_streams_cap, 452 sizeof(struct xen_front_cfg_stream), 453 GFP_KERNEL); 454 if (!pcm_instance->streams_cap) { 455 ret = -ENOMEM; 456 goto fail; 457 } 458 } 459 460 cur_pb = 0; 461 cur_cap = 0; 462 for (i = 0; i < num_streams; i++) { 463 ret = cfg_stream(front_info, pcm_instance, device_path, i, 464 &cur_pb, &cur_cap, stream_cnt); 465 if (ret < 0) 466 goto fail; 467 } 468 ret = 0; 469 470 fail: 471 kfree(device_path); 472 return ret; 473 } 474 475 int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info, 476 int *stream_cnt) 477 { 478 struct xenbus_device *xb_dev = front_info->xb_dev; 479 struct xen_front_cfg_card *cfg = &front_info->cfg; 480 int ret, num_devices, i; 481 char node[3]; 482 483 *stream_cnt = 0; 484 num_devices = 0; 485 do { 486 scnprintf(node, sizeof(node), "%d", num_devices); 487 if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node)) 488 break; 489 490 num_devices++; 491 } while (num_devices < SNDRV_PCM_DEVICES); 492 493 if (!num_devices) { 494 dev_warn(&xb_dev->dev, 495 "No devices configured for sound card at %s\n", 496 xb_dev->nodename); 497 return -ENODEV; 498 } 499 500 /* Start from default PCM HW configuration for the card. */ 501 cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw); 502 503 cfg->pcm_instances = 504 devm_kcalloc(&front_info->xb_dev->dev, num_devices, 505 sizeof(struct xen_front_cfg_pcm_instance), 506 GFP_KERNEL); 507 if (!cfg->pcm_instances) 508 return -ENOMEM; 509 510 for (i = 0; i < num_devices; i++) { 511 ret = cfg_device(front_info, &cfg->pcm_instances[i], 512 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt); 513 if (ret < 0) 514 return ret; 515 } 516 cfg->num_pcm_instances = num_devices; 517 return 0; 518 } 519 520
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.