linux/sound/xen/xen_snd_front_alsa.c
<<
>>
Prefs
   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 <linux/platform_device.h>
  12
  13#include <sound/core.h>
  14#include <sound/pcm.h>
  15#include <sound/pcm_params.h>
  16
  17#include <xen/xenbus.h>
  18
  19#include "xen_snd_front.h"
  20#include "xen_snd_front_alsa.h"
  21#include "xen_snd_front_cfg.h"
  22#include "xen_snd_front_evtchnl.h"
  23#include "xen_snd_front_shbuf.h"
  24
  25struct xen_snd_front_pcm_stream_info {
  26        struct xen_snd_front_info *front_info;
  27        struct xen_snd_front_evtchnl_pair *evt_pair;
  28        struct xen_snd_front_shbuf sh_buf;
  29        int index;
  30
  31        bool is_open;
  32        struct snd_pcm_hardware pcm_hw;
  33
  34        /* Number of processed frames as reported by the backend. */
  35        snd_pcm_uframes_t be_cur_frame;
  36        /* Current HW pointer to be reported via .period callback. */
  37        atomic_t hw_ptr;
  38        /* Modulo of the number of processed frames - for period detection. */
  39        u32 out_frames;
  40};
  41
  42struct xen_snd_front_pcm_instance_info {
  43        struct xen_snd_front_card_info *card_info;
  44        struct snd_pcm *pcm;
  45        struct snd_pcm_hardware pcm_hw;
  46        int num_pcm_streams_pb;
  47        struct xen_snd_front_pcm_stream_info *streams_pb;
  48        int num_pcm_streams_cap;
  49        struct xen_snd_front_pcm_stream_info *streams_cap;
  50};
  51
  52struct xen_snd_front_card_info {
  53        struct xen_snd_front_info *front_info;
  54        struct snd_card *card;
  55        struct snd_pcm_hardware pcm_hw;
  56        int num_pcm_instances;
  57        struct xen_snd_front_pcm_instance_info *pcm_instances;
  58};
  59
  60struct alsa_sndif_sample_format {
  61        u8 sndif;
  62        snd_pcm_format_t alsa;
  63};
  64
  65struct alsa_sndif_hw_param {
  66        u8 sndif;
  67        snd_pcm_hw_param_t alsa;
  68};
  69
  70static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
  71        {
  72                .sndif = XENSND_PCM_FORMAT_U8,
  73                .alsa = SNDRV_PCM_FORMAT_U8
  74        },
  75        {
  76                .sndif = XENSND_PCM_FORMAT_S8,
  77                .alsa = SNDRV_PCM_FORMAT_S8
  78        },
  79        {
  80                .sndif = XENSND_PCM_FORMAT_U16_LE,
  81                .alsa = SNDRV_PCM_FORMAT_U16_LE
  82        },
  83        {
  84                .sndif = XENSND_PCM_FORMAT_U16_BE,
  85                .alsa = SNDRV_PCM_FORMAT_U16_BE
  86        },
  87        {
  88                .sndif = XENSND_PCM_FORMAT_S16_LE,
  89                .alsa = SNDRV_PCM_FORMAT_S16_LE
  90        },
  91        {
  92                .sndif = XENSND_PCM_FORMAT_S16_BE,
  93                .alsa = SNDRV_PCM_FORMAT_S16_BE
  94        },
  95        {
  96                .sndif = XENSND_PCM_FORMAT_U24_LE,
  97                .alsa = SNDRV_PCM_FORMAT_U24_LE
  98        },
  99        {
 100                .sndif = XENSND_PCM_FORMAT_U24_BE,
 101                .alsa = SNDRV_PCM_FORMAT_U24_BE
 102        },
 103        {
 104                .sndif = XENSND_PCM_FORMAT_S24_LE,
 105                .alsa = SNDRV_PCM_FORMAT_S24_LE
 106        },
 107        {
 108                .sndif = XENSND_PCM_FORMAT_S24_BE,
 109                .alsa = SNDRV_PCM_FORMAT_S24_BE
 110        },
 111        {
 112                .sndif = XENSND_PCM_FORMAT_U32_LE,
 113                .alsa = SNDRV_PCM_FORMAT_U32_LE
 114        },
 115        {
 116                .sndif = XENSND_PCM_FORMAT_U32_BE,
 117                .alsa = SNDRV_PCM_FORMAT_U32_BE
 118        },
 119        {
 120                .sndif = XENSND_PCM_FORMAT_S32_LE,
 121                .alsa = SNDRV_PCM_FORMAT_S32_LE
 122        },
 123        {
 124                .sndif = XENSND_PCM_FORMAT_S32_BE,
 125                .alsa = SNDRV_PCM_FORMAT_S32_BE
 126        },
 127        {
 128                .sndif = XENSND_PCM_FORMAT_A_LAW,
 129                .alsa = SNDRV_PCM_FORMAT_A_LAW
 130        },
 131        {
 132                .sndif = XENSND_PCM_FORMAT_MU_LAW,
 133                .alsa = SNDRV_PCM_FORMAT_MU_LAW
 134        },
 135        {
 136                .sndif = XENSND_PCM_FORMAT_F32_LE,
 137                .alsa = SNDRV_PCM_FORMAT_FLOAT_LE
 138        },
 139        {
 140                .sndif = XENSND_PCM_FORMAT_F32_BE,
 141                .alsa = SNDRV_PCM_FORMAT_FLOAT_BE
 142        },
 143        {
 144                .sndif = XENSND_PCM_FORMAT_F64_LE,
 145                .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
 146        },
 147        {
 148                .sndif = XENSND_PCM_FORMAT_F64_BE,
 149                .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
 150        },
 151        {
 152                .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
 153                .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
 154        },
 155        {
 156                .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
 157                .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
 158        },
 159        {
 160                .sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
 161                .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
 162        },
 163        {
 164                .sndif = XENSND_PCM_FORMAT_MPEG,
 165                .alsa = SNDRV_PCM_FORMAT_MPEG
 166        },
 167        {
 168                .sndif = XENSND_PCM_FORMAT_GSM,
 169                .alsa = SNDRV_PCM_FORMAT_GSM
 170        },
 171};
 172
 173static int to_sndif_format(snd_pcm_format_t format)
 174{
 175        int i;
 176
 177        for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
 178                if (ALSA_SNDIF_FORMATS[i].alsa == format)
 179                        return ALSA_SNDIF_FORMATS[i].sndif;
 180
 181        return -EINVAL;
 182}
 183
 184static u64 to_sndif_formats_mask(u64 alsa_formats)
 185{
 186        u64 mask;
 187        int i;
 188
 189        mask = 0;
 190        for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
 191                if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)
 192                        mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
 193
 194        return mask;
 195}
 196
 197static u64 to_alsa_formats_mask(u64 sndif_formats)
 198{
 199        u64 mask;
 200        int i;
 201
 202        mask = 0;
 203        for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
 204                if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
 205                        mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);
 206
 207        return mask;
 208}
 209
 210static void stream_clear(struct xen_snd_front_pcm_stream_info *stream)
 211{
 212        stream->is_open = false;
 213        stream->be_cur_frame = 0;
 214        stream->out_frames = 0;
 215        atomic_set(&stream->hw_ptr, 0);
 216        xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
 217        xen_snd_front_shbuf_clear(&stream->sh_buf);
 218}
 219
 220static void stream_free(struct xen_snd_front_pcm_stream_info *stream)
 221{
 222        xen_snd_front_shbuf_free(&stream->sh_buf);
 223        stream_clear(stream);
 224}
 225
 226static struct xen_snd_front_pcm_stream_info *
 227stream_get(struct snd_pcm_substream *substream)
 228{
 229        struct xen_snd_front_pcm_instance_info *pcm_instance =
 230                        snd_pcm_substream_chip(substream);
 231        struct xen_snd_front_pcm_stream_info *stream;
 232
 233        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 234                stream = &pcm_instance->streams_pb[substream->number];
 235        else
 236                stream = &pcm_instance->streams_cap[substream->number];
 237
 238        return stream;
 239}
 240
 241static int alsa_hw_rule(struct snd_pcm_hw_params *params,
 242                        struct snd_pcm_hw_rule *rule)
 243{
 244        struct xen_snd_front_pcm_stream_info *stream = rule->private;
 245        struct device *dev = &stream->front_info->xb_dev->dev;
 246        struct snd_mask *formats =
 247                        hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
 248        struct snd_interval *rates =
 249                        hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 250        struct snd_interval *channels =
 251                        hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
 252        struct snd_interval *period =
 253                        hw_param_interval(params,
 254                                          SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
 255        struct snd_interval *buffer =
 256                        hw_param_interval(params,
 257                                          SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
 258        struct xensnd_query_hw_param req;
 259        struct xensnd_query_hw_param resp;
 260        struct snd_interval interval;
 261        struct snd_mask mask;
 262        u64 sndif_formats;
 263        int changed, ret;
 264
 265        /* Collect all the values we need for the query. */
 266
 267        req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
 268                                            (u64)(formats->bits[1]) << 32);
 269
 270        req.rates.min = rates->min;
 271        req.rates.max = rates->max;
 272
 273        req.channels.min = channels->min;
 274        req.channels.max = channels->max;
 275
 276        req.buffer.min = buffer->min;
 277        req.buffer.max = buffer->max;
 278
 279        req.period.min = period->min;
 280        req.period.max = period->max;
 281
 282        ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
 283                                                  &req, &resp);
 284        if (ret < 0) {
 285                /* Check if this is due to backend communication error. */
 286                if (ret == -EIO || ret == -ETIMEDOUT)
 287                        dev_err(dev, "Failed to query ALSA HW parameters\n");
 288                return ret;
 289        }
 290
 291        /* Refine HW parameters after the query. */
 292        changed  = 0;
 293
 294        sndif_formats = to_alsa_formats_mask(resp.formats);
 295        snd_mask_none(&mask);
 296        mask.bits[0] = (u32)sndif_formats;
 297        mask.bits[1] = (u32)(sndif_formats >> 32);
 298        ret = snd_mask_refine(formats, &mask);
 299        if (ret < 0)
 300                return ret;
 301        changed |= ret;
 302
 303        interval.openmin = 0;
 304        interval.openmax = 0;
 305        interval.integer = 1;
 306
 307        interval.min = resp.rates.min;
 308        interval.max = resp.rates.max;
 309        ret = snd_interval_refine(rates, &interval);
 310        if (ret < 0)
 311                return ret;
 312        changed |= ret;
 313
 314        interval.min = resp.channels.min;
 315        interval.max = resp.channels.max;
 316        ret = snd_interval_refine(channels, &interval);
 317        if (ret < 0)
 318                return ret;
 319        changed |= ret;
 320
 321        interval.min = resp.buffer.min;
 322        interval.max = resp.buffer.max;
 323        ret = snd_interval_refine(buffer, &interval);
 324        if (ret < 0)
 325                return ret;
 326        changed |= ret;
 327
 328        interval.min = resp.period.min;
 329        interval.max = resp.period.max;
 330        ret = snd_interval_refine(period, &interval);
 331        if (ret < 0)
 332                return ret;
 333        changed |= ret;
 334
 335        return changed;
 336}
 337
 338static int alsa_open(struct snd_pcm_substream *substream)
 339{
 340        struct xen_snd_front_pcm_instance_info *pcm_instance =
 341                        snd_pcm_substream_chip(substream);
 342        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 343        struct snd_pcm_runtime *runtime = substream->runtime;
 344        struct xen_snd_front_info *front_info =
 345                        pcm_instance->card_info->front_info;
 346        struct device *dev = &front_info->xb_dev->dev;
 347        int ret;
 348
 349        /*
 350         * Return our HW properties: override defaults with those configured
 351         * via XenStore.
 352         */
 353        runtime->hw = stream->pcm_hw;
 354        runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
 355                              SNDRV_PCM_INFO_MMAP_VALID |
 356                              SNDRV_PCM_INFO_DOUBLE |
 357                              SNDRV_PCM_INFO_BATCH |
 358                              SNDRV_PCM_INFO_NONINTERLEAVED |
 359                              SNDRV_PCM_INFO_RESUME |
 360                              SNDRV_PCM_INFO_PAUSE);
 361        runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;
 362
 363        stream->evt_pair = &front_info->evt_pairs[stream->index];
 364
 365        stream->front_info = front_info;
 366
 367        stream->evt_pair->evt.u.evt.substream = substream;
 368
 369        stream_clear(stream);
 370
 371        xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
 372
 373        ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
 374                                  alsa_hw_rule, stream,
 375                                  SNDRV_PCM_HW_PARAM_FORMAT, -1);
 376        if (ret) {
 377                dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
 378                return ret;
 379        }
 380
 381        ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 382                                  alsa_hw_rule, stream,
 383                                  SNDRV_PCM_HW_PARAM_RATE, -1);
 384        if (ret) {
 385                dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
 386                return ret;
 387        }
 388
 389        ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 390                                  alsa_hw_rule, stream,
 391                                  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 392        if (ret) {
 393                dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
 394                return ret;
 395        }
 396
 397        ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
 398                                  alsa_hw_rule, stream,
 399                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
 400        if (ret) {
 401                dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
 402                return ret;
 403        }
 404
 405        ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
 406                                  alsa_hw_rule, stream,
 407                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
 408        if (ret) {
 409                dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
 410                return ret;
 411        }
 412
 413        return 0;
 414}
 415
 416static int alsa_close(struct snd_pcm_substream *substream)
 417{
 418        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 419
 420        xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
 421        return 0;
 422}
 423
 424static int alsa_hw_params(struct snd_pcm_substream *substream,
 425                          struct snd_pcm_hw_params *params)
 426{
 427        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 428        int ret;
 429
 430        /*
 431         * This callback may be called multiple times,
 432         * so free the previously allocated shared buffer if any.
 433         */
 434        stream_free(stream);
 435
 436        ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev,
 437                                        &stream->sh_buf,
 438                                        params_buffer_bytes(params));
 439        if (ret < 0) {
 440                stream_free(stream);
 441                dev_err(&stream->front_info->xb_dev->dev,
 442                        "Failed to allocate buffers for stream with index %d\n",
 443                        stream->index);
 444                return ret;
 445        }
 446
 447        return 0;
 448}
 449
 450static int alsa_hw_free(struct snd_pcm_substream *substream)
 451{
 452        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 453        int ret;
 454
 455        ret = xen_snd_front_stream_close(&stream->evt_pair->req);
 456        stream_free(stream);
 457        return ret;
 458}
 459
 460static int alsa_prepare(struct snd_pcm_substream *substream)
 461{
 462        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 463
 464        if (!stream->is_open) {
 465                struct snd_pcm_runtime *runtime = substream->runtime;
 466                u8 sndif_format;
 467                int ret;
 468
 469                ret = to_sndif_format(runtime->format);
 470                if (ret < 0) {
 471                        dev_err(&stream->front_info->xb_dev->dev,
 472                                "Unsupported sample format: %d\n",
 473                                runtime->format);
 474                        return ret;
 475                }
 476                sndif_format = ret;
 477
 478                ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
 479                                                   &stream->sh_buf,
 480                                                   sndif_format,
 481                                                   runtime->channels,
 482                                                   runtime->rate,
 483                                                   snd_pcm_lib_buffer_bytes(substream),
 484                                                   snd_pcm_lib_period_bytes(substream));
 485                if (ret < 0)
 486                        return ret;
 487
 488                stream->is_open = true;
 489        }
 490
 491        return 0;
 492}
 493
 494static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
 495{
 496        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 497        int type;
 498
 499        switch (cmd) {
 500        case SNDRV_PCM_TRIGGER_START:
 501                type = XENSND_OP_TRIGGER_START;
 502                break;
 503
 504        case SNDRV_PCM_TRIGGER_RESUME:
 505                type = XENSND_OP_TRIGGER_RESUME;
 506                break;
 507
 508        case SNDRV_PCM_TRIGGER_STOP:
 509                type = XENSND_OP_TRIGGER_STOP;
 510                break;
 511
 512        case SNDRV_PCM_TRIGGER_SUSPEND:
 513                type = XENSND_OP_TRIGGER_PAUSE;
 514                break;
 515
 516        default:
 517                return -EINVAL;
 518        }
 519
 520        return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
 521}
 522
 523void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
 524                                       u64 pos_bytes)
 525{
 526        struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
 527        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 528        snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
 529
 530        cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
 531
 532        delta = cur_frame - stream->be_cur_frame;
 533        stream->be_cur_frame = cur_frame;
 534
 535        new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
 536        new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
 537        atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
 538
 539        stream->out_frames += delta;
 540        if (stream->out_frames > substream->runtime->period_size) {
 541                stream->out_frames %= substream->runtime->period_size;
 542                snd_pcm_period_elapsed(substream);
 543        }
 544}
 545
 546static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
 547{
 548        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 549
 550        return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
 551}
 552
 553static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
 554                             int channel, unsigned long pos, void __user *src,
 555                             unsigned long count)
 556{
 557        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 558
 559        if (unlikely(pos + count > stream->sh_buf.buffer_sz))
 560                return -EINVAL;
 561
 562        if (copy_from_user(stream->sh_buf.buffer + pos, src, count))
 563                return -EFAULT;
 564
 565        return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
 566}
 567
 568static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
 569                               int channel, unsigned long pos, void *src,
 570                               unsigned long count)
 571{
 572        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 573
 574        if (unlikely(pos + count > stream->sh_buf.buffer_sz))
 575                return -EINVAL;
 576
 577        memcpy(stream->sh_buf.buffer + pos, src, count);
 578
 579        return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
 580}
 581
 582static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
 583                              int channel, unsigned long pos, void __user *dst,
 584                              unsigned long count)
 585{
 586        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 587        int ret;
 588
 589        if (unlikely(pos + count > stream->sh_buf.buffer_sz))
 590                return -EINVAL;
 591
 592        ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
 593        if (ret < 0)
 594                return ret;
 595
 596        return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ?
 597                -EFAULT : 0;
 598}
 599
 600static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
 601                                int channel, unsigned long pos, void *dst,
 602                                unsigned long count)
 603{
 604        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 605        int ret;
 606
 607        if (unlikely(pos + count > stream->sh_buf.buffer_sz))
 608                return -EINVAL;
 609
 610        ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
 611        if (ret < 0)
 612                return ret;
 613
 614        memcpy(dst, stream->sh_buf.buffer + pos, count);
 615
 616        return 0;
 617}
 618
 619static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
 620                                int channel, unsigned long pos,
 621                                unsigned long count)
 622{
 623        struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 624
 625        if (unlikely(pos + count > stream->sh_buf.buffer_sz))
 626                return -EINVAL;
 627
 628        memset(stream->sh_buf.buffer + pos, 0, count);
 629
 630        return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
 631}
 632
 633/*
 634 * FIXME: The mmaped data transfer is asynchronous and there is no
 635 * ack signal from user-space when it is done. This is the
 636 * reason it is not implemented in the PV driver as we do need
 637 * to know when the buffer can be transferred to the backend.
 638 */
 639
 640static const struct snd_pcm_ops snd_drv_alsa_playback_ops = {
 641        .open           = alsa_open,
 642        .close          = alsa_close,
 643        .ioctl          = snd_pcm_lib_ioctl,
 644        .hw_params      = alsa_hw_params,
 645        .hw_free        = alsa_hw_free,
 646        .prepare        = alsa_prepare,
 647        .trigger        = alsa_trigger,
 648        .pointer        = alsa_pointer,
 649        .copy_user      = alsa_pb_copy_user,
 650        .copy_kernel    = alsa_pb_copy_kernel,
 651        .fill_silence   = alsa_pb_fill_silence,
 652};
 653
 654static const struct snd_pcm_ops snd_drv_alsa_capture_ops = {
 655        .open           = alsa_open,
 656        .close          = alsa_close,
 657        .ioctl          = snd_pcm_lib_ioctl,
 658        .hw_params      = alsa_hw_params,
 659        .hw_free        = alsa_hw_free,
 660        .prepare        = alsa_prepare,
 661        .trigger        = alsa_trigger,
 662        .pointer        = alsa_pointer,
 663        .copy_user      = alsa_cap_copy_user,
 664        .copy_kernel    = alsa_cap_copy_kernel,
 665};
 666
 667static int new_pcm_instance(struct xen_snd_front_card_info *card_info,
 668                            struct xen_front_cfg_pcm_instance *instance_cfg,
 669                            struct xen_snd_front_pcm_instance_info *pcm_instance_info)
 670{
 671        struct snd_pcm *pcm;
 672        int ret, i;
 673
 674        dev_dbg(&card_info->front_info->xb_dev->dev,
 675                "New PCM device \"%s\" with id %d playback %d capture %d",
 676                instance_cfg->name,
 677                instance_cfg->device_id,
 678                instance_cfg->num_streams_pb,
 679                instance_cfg->num_streams_cap);
 680
 681        pcm_instance_info->card_info = card_info;
 682
 683        pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
 684
 685        if (instance_cfg->num_streams_pb) {
 686                pcm_instance_info->streams_pb =
 687                                devm_kcalloc(&card_info->card->card_dev,
 688                                             instance_cfg->num_streams_pb,
 689                                             sizeof(struct xen_snd_front_pcm_stream_info),
 690                                             GFP_KERNEL);
 691                if (!pcm_instance_info->streams_pb)
 692                        return -ENOMEM;
 693        }
 694
 695        if (instance_cfg->num_streams_cap) {
 696                pcm_instance_info->streams_cap =
 697                                devm_kcalloc(&card_info->card->card_dev,
 698                                             instance_cfg->num_streams_cap,
 699                                             sizeof(struct xen_snd_front_pcm_stream_info),
 700                                             GFP_KERNEL);
 701                if (!pcm_instance_info->streams_cap)
 702                        return -ENOMEM;
 703        }
 704
 705        pcm_instance_info->num_pcm_streams_pb =
 706                        instance_cfg->num_streams_pb;
 707        pcm_instance_info->num_pcm_streams_cap =
 708                        instance_cfg->num_streams_cap;
 709
 710        for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
 711                pcm_instance_info->streams_pb[i].pcm_hw =
 712                        instance_cfg->streams_pb[i].pcm_hw;
 713                pcm_instance_info->streams_pb[i].index =
 714                        instance_cfg->streams_pb[i].index;
 715        }
 716
 717        for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
 718                pcm_instance_info->streams_cap[i].pcm_hw =
 719                        instance_cfg->streams_cap[i].pcm_hw;
 720                pcm_instance_info->streams_cap[i].index =
 721                        instance_cfg->streams_cap[i].index;
 722        }
 723
 724        ret = snd_pcm_new(card_info->card, instance_cfg->name,
 725                          instance_cfg->device_id,
 726                          instance_cfg->num_streams_pb,
 727                          instance_cfg->num_streams_cap,
 728                          &pcm);
 729        if (ret < 0)
 730                return ret;
 731
 732        pcm->private_data = pcm_instance_info;
 733        pcm->info_flags = 0;
 734        /* we want to handle all PCM operations in non-atomic context */
 735        pcm->nonatomic = true;
 736        strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
 737
 738        if (instance_cfg->num_streams_pb)
 739                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
 740                                &snd_drv_alsa_playback_ops);
 741
 742        if (instance_cfg->num_streams_cap)
 743                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
 744                                &snd_drv_alsa_capture_ops);
 745
 746        pcm_instance_info->pcm = pcm;
 747        return 0;
 748}
 749
 750int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
 751{
 752        struct device *dev = &front_info->xb_dev->dev;
 753        struct xen_front_cfg_card *cfg = &front_info->cfg;
 754        struct xen_snd_front_card_info *card_info;
 755        struct snd_card *card;
 756        int ret, i;
 757
 758        dev_dbg(dev, "Creating virtual sound card\n");
 759
 760        ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
 761                           sizeof(struct xen_snd_front_card_info), &card);
 762        if (ret < 0)
 763                return ret;
 764
 765        card_info = card->private_data;
 766        card_info->front_info = front_info;
 767        front_info->card_info = card_info;
 768        card_info->card = card;
 769        card_info->pcm_instances =
 770                        devm_kcalloc(dev, cfg->num_pcm_instances,
 771                                     sizeof(struct xen_snd_front_pcm_instance_info),
 772                                     GFP_KERNEL);
 773        if (!card_info->pcm_instances) {
 774                ret = -ENOMEM;
 775                goto fail;
 776        }
 777
 778        card_info->num_pcm_instances = cfg->num_pcm_instances;
 779        card_info->pcm_hw = cfg->pcm_hw;
 780
 781        for (i = 0; i < cfg->num_pcm_instances; i++) {
 782                ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
 783                                       &card_info->pcm_instances[i]);
 784                if (ret < 0)
 785                        goto fail;
 786        }
 787
 788        strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
 789        strncpy(card->shortname, cfg->name_short, sizeof(card->shortname));
 790        strncpy(card->longname, cfg->name_long, sizeof(card->longname));
 791
 792        ret = snd_card_register(card);
 793        if (ret < 0)
 794                goto fail;
 795
 796        return 0;
 797
 798fail:
 799        snd_card_free(card);
 800        return ret;
 801}
 802
 803void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
 804{
 805        struct xen_snd_front_card_info *card_info;
 806        struct snd_card *card;
 807
 808        card_info = front_info->card_info;
 809        if (!card_info)
 810                return;
 811
 812        card = card_info->card;
 813        if (!card)
 814                return;
 815
 816        dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
 817                card->number);
 818        snd_card_free(card);
 819
 820        /* Card_info will be freed when destroying front_info->xb_dev->dev. */
 821        card_info->card = NULL;
 822}
 823